个人理解
通过mockito给程序设定一个预期值,然后通过mockito仿真执行程序,看执行逻辑输出是否符合预期的结果。主要用于检测逻辑是否正确。由于不是真的执行,因此会隔离真实环境。无法测试底层调用或者sql是否存在问题。
mockito 资源
官网: http://mockito.org
API文档:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html
项目源码:https://github.com/mockito/mockito
依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
注解
注解 | 作用 | 例子 |
---|---|---|
@PowerMockIgnore | 忽略一些模块 | @PowerMockIgnore("javax.management.*") |
@PrepareForTest | mock静态类 | @PrepareForTest({NumberUtils.class}) |
@RunWith | 启动注解,使用什么来运行程序 | @RunWith(MockitoJUnitRunner.class) |
注意事项
- @PowerMockIgnore("javax.management.*")
由于PowerMock的工做原理便是使用自定义的类加载器来加载被修改过的类,从而达到打桩的目的,使用Powermock后会提示classloader错误,所以待测试类中使用到了XML解析相关的包和类,那么测试类前一样须要增长@PowerMockIgnore({"org.xml.", "javax.xml."}),消除类加载器引入报错。
- @PrepareForTest({NumberUtils.class})
把静态方法mock掉,模拟调用静态方法,返回一个给定的值。
PowerMockito.mockStatic(NumberUtils.class);
when(NumberUtils.change()).thenReturn("123");
- 调用无返回的方法
PowerMockito.doNothing().when(casService).addSupplier(anyLong(), any(ServiceKey.class));
连续调用
@Test(expected = RuntimeException.class)
public void continuousCallTest() {
// 模拟连续调用返回指望值,若是分开,则只有最后一个有效
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(new RuntimeException());
Assert.assertEquals("1", exampleServiceMock.aTest());
Assert.assertEquals("2", exampleServiceMock.aTest());
Assert.assertEquals("3", exampleServiceMock.aTest());
Assert.assertEquals("4", exampleServiceMock.aTest());
// 第三次或更多调用都会抛出异常
exampleServiceMock.aTest();
}
ExampleServiceImplTest
import com.jd.work.example.service.ExampleService;
import com.jd.work.example.utils.RedisCache;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
@Slf4j
@RunWith(MockitoJUnitRunner.class)
//@RunWith(PowerMockRunner.class)
@PrepareForTest()
@PowerMockIgnore("javax.management.*")
public class ExampleServiceImplTest {
/**
* 待测试的具体实现类
*/
@InjectMocks
private ExampleServiceImpl exampleService;
@Mock
private RedisCache redisCache;
/**
* 调用了自身接口的其他方法
*/
@Mock
private ExampleService exampleServiceMock;
@Before
public void setUp() {
// mock注解初始化,不加会报错
MockitoAnnotations.initMocks(this);
}
@Test
public void example() {
PowerMockito.when(exampleServiceMock.bTest()).thenReturn("ok-b");
String s = exampleService.aTest();
Assert.assertEquals("ok-b", s);
}
@Test(expected = RuntimeException.class)
public void continuousCallTest() {
// 模拟连续调用返回指望值,若是分开,则只有最后一个有效
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("2");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("3");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("4");
PowerMockito.when(exampleServiceMock.aTest()).thenReturn("1").thenReturn("2").thenThrow(new RuntimeException());
Assert.assertEquals("1", exampleServiceMock.aTest());
Assert.assertEquals("2", exampleServiceMock.aTest());
Assert.assertEquals("3", exampleServiceMock.aTest());
Assert.assertEquals("4", exampleServiceMock.aTest());
// 第三次或更多调用都会抛出异常
exampleServiceMock.aTest();
}
@Mock
private List<String> list;
@Test
public void test() {
list.add("test");
verify(list).add("test");
verify(list, atLeastOnce()).add("test");
verify(list, atLeast(1)).add("test");
verify(list, atMost(2)).add("test");
verify(list, never()).add("test111");
assertThat(0, equalTo(list.size()));
}
@Test
public void test3() {
list.add("test");
verify(list).add("test");
// open will fail
// list.clear();
// 代表上一次verify之后再无与list的交互
verifyNoMoreInteractions(list);
}
@Test
public void test4() {
list.add("test");
// 自始至终都与list无任何交互
verifyZeroInteractions(list);
}
}
Mock静态方法
测试类头部需要加@PrepareForTest({StaticClass.class})
@RunWith(PowerMockRunner.class)
@PrepareForTest({StaticClass.class})
public class ExampleTest {
@Test
public void test() {
PowerMockito.mockStatic(StaticClass.class);
}
}
Mock私有方法1
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
public class ExampleTest {
@InjectMocks
private ExampleServiceImpl exampleService;
@Test
public void testPrivateMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<?>[] params = new Class<?>[]{String.class, Map.class};
Map<String, Object> paramsMap = new HashMap<>(3);
paramsMap.put("asf_id", "123");
paramsMap.put("asf_urge_time", "22222");
paramsMap.put("asf_apply_time", "24141211");
Method method = exampleService.getClass().getDeclaredMethod("handleMessageTemplate", params);
//making private method accessible
method.setAccessible(true);
assertEquals("ASF_URGE_SECOND_TEMPLATE_DEFAULT", method.invoke(exampleService, "ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap));
method.invoke(exampleService, "", paramsMap);
}
}
Mock无返回方法1
@RunWith(PowerMockRunner.class)
@PowerMockIgnore("javax.management.*")
public class ExampleTest {
@Mock
private RedisCache redisCache;
@Test
public void methodNoReturnTest(){
PowerMockito.doAnswer(invocation -> {
Object[] args = invocation.getArguments();
return "called with arguments: " + Arrays.toString(args);
}).when(redisCache).zAdd("key", "xx", 30);
}
}
Mock抛出异常
// 有返回值抛异常
Mockito.when(mockitoTestModel.returnString()).thenThrow(new MyException());
// 无返回值抛异常
Mockito.doThrow(new MyException("TEST")).when(mockitoTestModel).noReturn();
Mock私有方法2
// 私有方法测试,无参
Method method = PowerMockito.method(TestService.class, "methodName");
method.invoke(testService);
// 私有方法测试,传参
Method method = PowerMockito.method(TestService.class, "methodName", Test1.class, Test2.class);
method.invoke(testService, test1, test2);
@Test
public void testPrivateMethod2() {
try {
Map<String, Object> paramsMap = new HashMap<>(3);
paramsMap.put("asf_id", "123");
paramsMap.put("asf_urge_time", "22222");
paramsMap.put("asf_apply_time", "24141211");
Method method = PowerMockito.method(ExampleServiceImpl.class, "handleAnnotation", String.class, Map.class);
method.invoke(exampleService, "ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap);
//making private method accessible
assertEquals("ASF_URGE_SECOND_TEMPLATE_DEFAULT", method.invoke(exampleService, "ASF_URGE_SECOND_TEMPLATE_DEFAULT", paramsMap));
method.invoke(exampleService, "", paramsMap);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
Mock无返回方法2
@Test
public void whenAddCalledVerfied() {
List myList = mock(List.class);
Mockito.doNothing().when(myList).add(isA(Integer.class), isA(String.class));
myList.add(0, "");
verify(myList, times(1)).add(0, "");
}