摘要:例子使用源代码我们先给了一个的实现然后又规定了方法的返回值。源代码也就是说,得益于,我们能够很方便地对依赖关系中任意层级的任意做。
Github地址
Mock测试技术能够避免你为了测试一个方法,却需要自行构建整个依赖关系的工作,并且能够让你专注于当前被测试对象的逻辑,而不是其依赖的其他对象的逻辑。
举例来说,比如你需要测试Foo.methodA,而这个方法依赖了Bar.methodB,又传递依赖到了Zoo.methodC,于是它们的依赖关系就是Foo->Bar->Zoo,所以在测试代码里你必须自行new Bar和Zoo。
有人会说:"我直接用Spring的DI机制不就行了吗?"的确,你可以用Spring的DI机制,不过解决不了测试代码耦合度过高的问题:
因为Foo方法内部调用了Bar和Zoo的方法,所以你对其做单元测试的时候,必须完全了解Bar和Zoo方法的内部逻辑,并且谨慎的传参和assert结果,一旦Bar和Zoo的代码修改了,你的Foo测试代码很可能就会运行失败。
所以这个时候我们需要一种机制,能过让我们在测试Foo的时候不依赖于Bar和Zoo的具体实现,即不关心其内部逻辑,只关注Foo内部的逻辑,从而将Foo的每个逻辑分支都测试到。
所以业界就产生了Mock技术,它可以让我们做一个假的Bar(不需要Zoo,因为只有真的Bar才需要Zoo),然后控制这个假的Bar的行为(让它返回什么就返回什么),以此来测试Foo的每个逻辑分支。
你肯定会问,这样的测试有意义吗?在真实环境里Foo用的是真的Bar而不是假的Bar,你用假的Bar测试成功能代表真实环境不出问题?
其实假Bar代表的是一个行为正确的Bar,用它来测试就能验证"在Bar行为正确的情况下Foo的行为是否正确",而真Bar的行为是否正确会由它自己的测试代码来验证。
Mock技术的另一个好处是能够让你尽量避免集成测试,比如我们可以Mock一个Repository(数据库操作类),让我们尽量多写单元测试,提高测试代码执行效率。
spring-boot-starter-test依赖了Mockito,所以我们会在本章里使用Mockito来讲解。
被测试类先介绍一下接下来要被我们测试的类Foo、Bar俩兄弟。
public interface Foo { boolean checkCodeDuplicate(String code); } public interface Bar { Set例子1: 不使用Mock技术getAllCodes(); } @Component public class FooImpl implements Foo { private Bar bar; @Override public boolean checkCodeDuplicate(String code) { return bar.getAllCodes().contains(code); } @Autowired public void setBar(Bar bar) { this.bar = bar; } }
源代码NoMockTest:
public class NoMockTest { @Test public void testCheckCodeDuplicate1() throws Exception { FooImpl foo = new FooImpl(); foo.setBar(new Bar() { @Override public SetgetAllCodes() { return Collections.singleton("123"); } }); assertEquals(foo.checkCodeDuplicate("123"), true); } @Test public void testCheckCodeDuplicate2() throws Exception { FooImpl foo = new FooImpl(); foo.setBar(new FakeBar(Collections.singleton("123"))); assertEquals(foo.checkCodeDuplicate("123"), true); } public class FakeBar implements Bar { private final Set codes; public FakeBar(Set codes) { this.codes = codes; } @Override public Set getAllCodes() { return codes; } } }
这个测试代码里用到了两种方法来做假的Bar:
匿名内部类
做了一个FakeBar
这两种方式都不是很优雅,看下面使用Mockito的例子。
例子2:使用Mockito源代码MockitoTest:
public class MockitoTest { @Mock private Bar bar; @InjectMocks private FooImpl foo; @BeforeMethod(alwaysRun = true) public void initMock() { MockitoAnnotations.initMocks(this); } @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }
我们先给了一个Bar的Mock实现:@Mock private Bar bar;
然后又规定了getAllCodes方法的返回值:when(bar.getAllCodes()).thenReturn(Collections.singleton("123"))。这样就把一个假的Bar定义好了。
最后利用Mockito把Bar注入到Foo里面,@InjectMocks private FooImpl foo;、MockitoAnnotations.initMocks(this);
例子3:配合Spring Test源代码Spring_1_Test:
@ContextConfiguration(classes = FooImpl.class) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Spring_1_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Foo foo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }
要注意,如果要启用Spring和Mockito,必须添加这么一行:@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)。
例子4:配合Spring Test(多层依赖)当Bean存在这种依赖关系当时候:LooImpl -> FooImpl -> Bar,我们应该怎么测试呢?
按照Mock测试的原则,这个时候我们应该mock一个Foo对象,把这个注入到LooImpl对象里,就像例子3里的一样。
不过如果你不想mock Foo而是想mock Bar的时候,其实做法和前面也差不多,Spring会自动将mock Bar注入到FooImpl中,然后将FooImpl注入到LooImpl中。
源代码Spring_2_Test:
@ContextConfiguration(classes = { FooImpl.class, LooImpl.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Spring_2_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Loo loo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(loo.checkCodeDuplicate("123"), true); } }
也就是说,得益于Spring Test Framework,我们能够很方便地对依赖关系中任意层级的任意Bean做mock。
例子5:配合Spring Boot Test源代码Boot_1_Test:
@SpringBoot_1_Test(classes = { FooImpl.class }) @TestExecutionListeners(listeners = MockitoTestExecutionListener.class) public class Boot_1_Test extends AbstractTestNGSpringContextTests { @MockBean private Bar bar; @Autowired private Foo foo; @Test public void testCheckCodeDuplicate() throws Exception { when(bar.getAllCodes()).thenReturn(Collections.singleton("123")); assertEquals(foo.checkCodeDuplicate("123"), true); } }参考文档
Spring Framework Testing
Spring Boot Testing
Mockito
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67442.html
摘要:首先先来看我们事先定义的以及。可以看到会修改方法的返回值,使其返回。例子测试的行为最简单的测试方法就是直接调用,看看它是否使用返回。先看这段代码这些是利用提供的和来判断是否被代理了的实现是通过动态代理来做的。 Github地址 Spring提供了一套AOP工具,但是当你把各种Aspect写完之后,如何确定这些Aspect都正确的应用到目标Bean上了呢?本章将举例说明如何对Spring...
摘要:地址提供了,能够很方便的来测试。同时也提供了更进一步简化了测试需要的配置工作。本章节将分别举例说明在不使用和使用下如何对进行测试。例子测试的关键是使用对象,利用它我们能够在不需启动容器的情况下测试的行为。 Github地址 Spring Testing Framework提供了Spring MVC Test Framework,能够很方便的来测试Controller。同时Spring...
摘要:单元测试因为公司单元测试覆盖率需要达到,所以进行单元测试用例编写。测试的时候可以把每个判断分支都走到。同这句代码,可以通过如此一个对象,使用以上方法基本上可以编写所有代码的测试类。编写测试一定程度上可以发现代码错误,可以借此重构代码。 3.springboot单元测试因为公司单元测试覆盖率需要达到80%,所以进行单元测试用例编写。多模块项目的因为会经常调用其他服务,而且避免数据库操作对...
摘要:因为只有这样才能够在测试环境下发现生产环境的问题,也避免出现一些因为配置不同导致的奇怪问题。而方法则能够不改变原有配置不提供新的配置的情况下,就能够关闭。 Github地址 在Chapter 1: 基本用法 - 使用Spring Boot Testing工具里提到: 除了单元测试(不需要初始化ApplicationContext的测试)外,尽量将测试配置和生产配置保持一致。比如如果生产...
摘要:地址是提供的方便测试序列化反序列化的测试工具,在的文档中有一些介绍。例子简单例子源代码见使用通包下的文件测试结果是否正确或者使用基于的校验例子测试可以用来测试。这个例子里使用了自定义的测试代码例子使用事实上也可以配合一起使用。 Github地址 @JsonTest是Spring Boot提供的方便测试JSON序列化反序列化的测试工具,在Spring Boot的文档中有一些介绍。 需要注...
阅读 3386·2021-09-22 16:00
阅读 3432·2021-09-07 10:26
阅读 2899·2019-08-30 15:55
阅读 2830·2019-08-30 13:48
阅读 1345·2019-08-30 12:58
阅读 2140·2019-08-30 11:15
阅读 920·2019-08-30 11:08
阅读 484·2019-08-29 18:41