网站设计模板psd,深圳网站开发哪家专业,淘宝指数网站,网站建设工作室wp主题模板从使用Spring 2.5开始#xff0c;我从基于XML的应用程序上下文切换到了注释。 尽管我发现那些非常有用且节省大量时间的人#xff0c;但我始终觉得在灵活性方面我失去了一些东西。 特别是Autowired批注-或标准Inject-在我看来就像新的“新”#xff0c;增加了类之间的耦合我从基于XML的应用程序上下文切换到了注释。 尽管我发现那些非常有用且节省大量时间的人但我始终觉得在灵活性方面我失去了一些东西。 特别是Autowired批注-或标准Inject-在我看来就像新的“新”增加了类之间的耦合并使得在需要时更难以更改实现。 我仍然有这种感觉但是我已经学到了一种有趣的模式来限制测试代码时的问题即当我想将bean的真实实现替换为模拟时。 让我们用一个例子来说明。 我想构建一个应用程序以便为我在网络上找到有趣的东西。 我将从一个接受URL的服务开始如果它是一个有趣的新URL则将其添加书签。 直到最近我可能已经编写了如下代码 Named
public class AwesomenessFinder {Injectprivate BlogAnalyzer blogAnalyzer;Injectprivate BookmarkService bookmarkService;public void checkBlog(String url) {if (!bookmarkService.contains(url) blogAnalyzer.isInteresting(url)) {bookmarkService.bookmark(url);}}
} 不好你明白为什么吗 如果没有请继续阅读希望您今天能学到一些有用的东西。 因为我很认真所以我想为此代码创建单元测试。 希望我的算法很好但是我想确保它不会为无聊的博客添加书签或将相同的URL添加为书签两次。 那就是问题所在我想将AwesomenessFinder与它的依赖隔离开来。 如果我使用的是XML配置则可以在测试上下文中简单地注入模拟实现是否可以使用批注来实现 嗯是 有一种方法带有Primary批注。 让我们尝试为BlogAnalyzer和BookmarkService创建模拟实现。 Named
Primary
public class BlogAnalyzerMock implements BlogAnalyzer {public boolean isInteresting(String url) {return true;}
}Named
Primary
public class BookmarkServiceMock implements BookmarkService {Set bookmarks new HashSet();public boolean contains(String url) {return bookmarks.contains(url);}public void bookmark(String url) {bookmarks.add(url);}
} 因为我使用Maven并将这些模拟放置在test / java目录中所以主应用程序将看不到它们并将注入实际的实现。 另一方面单元测试将看到2种实现。 Primary是必需的以防止出现类似以下的异常 org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [service.BlogAnalyzer] is defined: expected single matching bean
but found 2: [blogAnalyzerMock, blogAnalyzerImpl] 现在我可以测试我的算法了 RunWith(SpringJUnit4ClassRunner.class)
ContextConfiguration(locations classpath:application-context.xml)
public class AwesomenessFinderTest {Injectprivate AwesomenessFinder awesomenessFinder;Injectprivate BookmarkService bookmarkService;Testpublic void checkInterestingBlog_bookmarked() {String url http://www.javaspecialists.eu;assertFalse(bookmarkService.contains(url));awesomenessFinder.checkBlog(url);assertTrue(bookmarkService.contains(url));}
} 不错我测试了幸福的道路一个有趣的博客被加了书签。 现在我该如何测试其他情况。 当然我可以在模拟中添加一些逻辑以查找某些已添加书签或不感兴趣的URL但这会变得笨拙。 这是一个非常简单的算法想象一下测试更复杂的东西有多糟糕。 有一种更好的方法需要重新设计我的类以及注入依赖项的方法。 方法如下 Named
public class AwesomenessFinder {private BlogAnalyzer blogAnalyzer;private BookmarkService bookmarkService;Injectpublic AwesomenessFinder(BlogAnalyzer blogAnalyzer, BookmarkService bookmarkService) {this.blogAnalyzer blogAnalyzer;this.bookmarkService bookmarkService;}public void checkBlog(String url) {if (!bookmarkService.contains(url) blogAnalyzer.isInteresting(url)) {bookmarkService.bookmark(url);}}
} 请注意我仍然使用Inject注释自动关联我的依赖项因此AwesomenessFinder的调用者不会受到影响。 例如客户端类中的以下内容仍然有效 Inject
private AwesomenessFinder awesomenessFinder; 但是最大的不同是我在构造函数级别自动装配这为我提供了一种注入模拟实现的干净方法。 而且由于我们是在模拟所以我们使用一个模拟库。 去年我写了一篇有关嘲讽的文章其中我使用了丑陋的二传手来注入嘲讽。 使用这里提到的技术我不再需要暴露依赖项我得到了更好的封装。 这是更新后的测试用例的样子 public class AwesomenessFinderTest {Testpublic void checkInterestingBlog_bookmarked() {BookmarkService bookmarkService mock(BookmarkService.class);when(bookmarkService.contains(anyString())).thenReturn(false);BlogAnalyzer blogAnalyzer mock(BlogAnalyzer.class);when(blogAnalyzer.isInteresting(anyString())).thenReturn(true);AwesomenessFinder awesomenessFinder new AwesomenessFinder(blogAnalyzer, bookmarkService);String url http://www.javaspecialists.eu;awesomenessFinder.checkBlog(url);verify(bookmarkService).bookmark(url);}
} 请注意现在这是纯Java语言无需使用Spring注入模拟。 而且这些模拟的定义与它们的用法位于同一位置从而简化了维护。 为了更进一步让我们实现其他测试用例。 为了避免代码重复我们将重构测试类并引入一些枚举以使测试用例尽可能地表达。 public class AwesomenessFinderTest {private enum Knowledge {KNOWN, UNKNOWN};private enum Quality {INTERESTING, BORING};private enum ExpectedBookmark {STORED, IGNORED}private enum ExpectedAnalysis {ANALYZED, SKIPPED}Testpublic void checkInterestingBlog_bookmarked() {checkCase(Knowledge.UNKNOWN, Quality.INTERESTING,ExpectedBookmark.STORED, ExpectedAnalysis.ANALYZED);}Testpublic void checkBoringBlog_ignored() {checkCase(Knowledge.UNKNOWN, Quality.BORING,ExpectedBookmark.IGNORED, ExpectedAnalysis.ANALYZED);}Testpublic void checkKnownBlog_ignored() {checkCase(Knowledge.KNOWN, Quality.INTERESTING,ExpectedBookmark.IGNORED, ExpectedAnalysis.SKIPPED);}private void checkCase(Knowledge knowledge, Quality quality,ExpectedBookmark expectedBookmark, ExpectedAnalysis expectedAnalysis) {BookmarkService bookmarkService mock(BookmarkService.class);boolean alreadyBookmarked (knowledge Knowledge.KNOWN) ? true : false;when(bookmarkService.contains(anyString())).thenReturn(alreadyBookmarked);BlogAnalyzer blogAnalyzer mock(BlogAnalyzer.class);boolean interesting (quality Quality.INTERESTING) ? true : false;when(blogAnalyzer.isInteresting(anyString())).thenReturn(interesting);AwesomenessFinder awesomenessFinder new AwesomenessFinder(blogAnalyzer, bookmarkService);String url whatever;awesomenessFinder.checkBlog(url);if (expectedBookmark ExpectedBookmark.STORED) {verify(bookmarkService).bookmark(url);} else {verify(bookmarkService, never()).bookmark(url);}if (expectedAnalysis ExpectedAnalysis.ANALYZED) {verify(blogAnalyzer).isInteresting(url);} else {verify(blogAnalyzer, never()).isInteresting(url);}}
} 最后但并非最不重要的一点是构造函数注入的一个不错的好处是能够将类的所有依赖项放在同一位置构造函数。 如果依赖项列表超出了控制范围则构造函数的大小会产生非常明显的代码味道。 这表明您在班级中肯定承担了多个责任您应该将其划分为多个班级以便于进行单元测试更容易隔离。 参考 自动装配或不自动装配从我们JCG的合作伙伴达勒帕热的编程和更多的博客。 翻译自: https://www.javacodegeeks.com/2013/04/spring-to-autowire-or-not-to-autowire.html