Java单元测试对void方法的测试
Posted FserSuN
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java单元测试对void方法的测试相关的知识,希望对你有一定的参考价值。
1 背景介绍
日常系统单测开发都是通过对方法的返回值进行验证。而void方法没有返回值,这是我们可以对其行为进行验证。下面是几个常见的例子,被验证方法均是void方法。
- 方法内部依赖外部接口,当外部接口超时会最多会重试3次。3次全部重试失败则会抛出异常
- 方法内部负责聚合外部接口,关键的外部接口每个仅调用一次。
这时我们就可以通过mock并验证这组行为是否发生。在java项目中一般通过mockito来实现。
2 使用mockito进行测试
首先我们准备好要用到的测试类。
public class MyList extends AbstractList<String>
@Override
public void add(int index, String element)
// no-op
2.1 对void方法进行mock及行为验证
我们创建了MyList的Mock对象,这里用到了doNothing,myList的void方法被调用,并传入一个整数和字符串时。
@Test
public void whenAddCalledVerified()
MyList myList = Mockito.mock(MyList.class);
Mockito.doNothing().when(myList).add(Mockito.isA(Integer.class), Mockito.isA(String.class));
myList.add(0, "");
Mockito.verify(myList, Mockito.times(1)).add(0, "");
方法是Mockito中对void方法会默认完成doNothing的调用,实现void方法的插桩,因此还可以简化写为如下形式。
@Test
public void whenAddCalledVerified()
MyList myList = Mockito.mock(MyList.class);
myList.add(0, "");
Mockito.verify(myList, Mockito.times(1)).add(0, "");
此外还可通过doThrow方法对异常抛出进行模拟,
@Test(expected = Exception.class)
public void givenNull_addThrows()
MyList myList = Mockito.mock(MyList.class);
Mockito.doThrow().when(myList).add(Mockito.isA(Integer.class), Mockito.isNull());
myList.add(0, null);
2.2 参数捕获
前面我们用到了doNothing时,如果就是验证void方法的默认行为,可以省略doNothing。当我们需要捕获参数时,我们可以对默认行为调整。例如验证传入void方法的参数是否符合预期。这时就可以覆盖默认行为,进行参数捕获,最终完成验证。
@Test
public void whenAddCalledValueCaptured()
MyList myList = Mockito.mock(MyList.class);
ArgumentCaptor<String> valueCapture = ArgumentCaptor.forClass(String.class);
Mockito.doNothing().when(myList).add(Mockito.any(Integer.class), valueCapture.capture());
myList.add(0, "captured");
Assert.assertEquals("captured", valueCapture.getValue());
2.3 使用Answer对象对void方法调用与返回逻辑进行定制验证
一个方法的行为通常会很复杂,不会像add或set方法一样简单。这种场景我们可以使用Mockito的Answer方法添加我们需要的行为。
@Test
public void whenAddCalledAnswered()
MyList myList = Mockito.mock(MyList.class);
Mockito.doAnswer(invocation ->
Object arg0 = invocation.getArgument(0);
Object arg1 = invocation.getArgument(1);
Assert.assertEquals(3, arg0);
Assert.assertEquals("answer me", arg1);
return null;
).when(myList).add(Mockito.any(Integer.class), Mockito.any(String.class));
myList.add(3, "answer me");
这里我们通过answer获取调用的入参并进行验证。同理还可以基于invocation对象做其它的事情。
2.4 部分mock
部分mock即我们mock对象后,某些方法调用是直接调用真实方法而不是调用创建的桩模拟的方法。这里我们用到doCallRealMethod(),最后当myList.add被调用,这时是实际的方法被调用。
@Test
public void whenAddCalledRealMethodCalled()
MyList myList = Mockito.mock(MyList.class);
Mockito.doCallRealMethod().when(myList).add(Mockito.any(Integer.class), Mockito.any(String.class));
myList.add(1, "real");
Mockito.verify(myList,Mockito. times(1)).add(1, "real");
2.5 mock方法内部依赖外部对象方法的验证
@Component
public class MyHandler
@AutoWired
private MyDependency myDependency;
public int someMethod()
...
return anotherMethod();
public int anotherMethod() ...
这种场景,进行mock测试,我们一般使用InjectMocks注解注入依赖,对MyHandler进行测试。同时使用Mock注解创建MyDependency的mock对象。但此时通过verify进行验证时候会产生错误。
@RunWith(MockitoJUnitRunner.class
class MyHandlerTest
@InjectMocks
private MyHandler myHandler;
@Mock
private MyDependency myDependency;
@Test
public void testSomeMethod()
when(myHandler.anotherMethod()).thenReturn(1);
assertEquals(myHandler.someMethod() == 1);
此时需要结合 @Spy注解与@InjectMocks,实现Mock对象的创建及注解注入,这样即可以实现对含有依赖的void方法进行行为验证。
@RunWith(MockitoJUnitRunner.class)
class MyHandlerTest
@Spy
@InjectMocks
private MyHandler myHandler;
@Mock
private MyDependency myDependency;
@Test
public void testSomeMethod()
doReturn(1).when(myHandler).anotherMethod();
assertEquals(myHandler.someMethod() == 1);
verify(myHandler, times(1)).anotherMethod();
2.6 Mockito.any()模拟任意输入
当我们调用方法时,只关注方法被调用,而某个参数具体是什么我们不关注,这时可以使用Mockito.any()方法。
verify(dao).send(eq(user), any());
3 参考
[1]https://stackoverflow.com/questions/30774358/how-can-i-mock-methods-of-injectmocks-class
[2]https://www.baeldung.com/mockito-void-methods
以上是关于Java单元测试对void方法的测试的主要内容,如果未能解决你的问题,请参考以下文章