Mockito 学习
描述
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
下面是进行总结的一些使用情况的场景:
依赖包
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-all -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
自动装配三种方式
- 通过对进行注解
@RunWith(MockitoJUnitRunner.class)
public class MockByRunnerTest {}
- 通过在方法中进行调用初始化
public class MockByAnnotationTest {
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
}
- 通过使用Rule进行注解
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
常见的命令
深度Mock ,可以让其它对象继承Mock属性
public class DeepMockTest {
public class Lession03Service {
public Lession03 get(){
throw new RuntimeException();
}
}
public class Lession03 {
public void foo(){
throw new RuntimeException();
}
}
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Lession03Service lession03Service;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testDeepMock(){
Lession03 lession03 = lession03Service.get();
lession03.foo();
}
}
常用的Stubbing 操作
@RunWith(MockitoJUnitRunner.class)
public class StubbingTest {
public class StubbingService {
public int getI(){
System.out.println("===== get I ======");
return 10;
}
public String getS(){
System.out.println("========= get S ==========");
throw new RuntimeException();
}
}
private List<String> list;
@Before
public void init(){
this.list = mock(ArrayList.class);
}
@Test
public void hosToUseStubbing(){
when(list.get(0)).thenReturn("first");
assert list.get(0).equals("first");
when(list.get((anyInt()))).thenThrow(new RuntimeException());
try{
list.get(0);
fail();
}catch (Exception e){
assert e.getClass().equals(RuntimeException.class);
}
}
@Test
public void howToStubbingVoidMethod(){
doNothing().when(list).clear();
list.clear();
verify(list, times(1)).clear();
doThrow(RuntimeException.class).when(list).clear();
try{
list.clear();
fail();
}catch (Exception e){
assert e.getClass().equals(RuntimeException.class);
}
}
@Test
public void stubbingDoReturn(){
when(list.get(0)).thenReturn("first");
doReturn("second").when(list).get(1);
assert list.get(0).equals("first");
assert list.get(1).equals("second");
}
@Test
public void iterateSubbing() {
when(list.size()).thenReturn(1,2,3,4);
assert list.size() == 1;
assert list.size() == 2;
assert list.size() == 3;
assert list.size() == 4;
assert list.size() == 4;
}
@Test
public void stubbingWithAnswer(){
when(list.get(anyInt())).thenAnswer(invocationOnMock -> {
Integer index = invocationOnMock.getArgument(0, Integer.class);
return String.valueOf(index * 10);
});
assert list.get(0).equals("0");
assert list.get(999).equals("9990");
}
@Test
public void stubbingWithRealCall(){
StubbingService stubbingService = mock(StubbingService.class);
when(stubbingService.getS()).thenReturn("Alex");
assert stubbingService.getS().equals("Alex");
when(stubbingService.getI()).thenCallRealMethod();
assert stubbingService.getI() == 10;
}
@After
public void destroy(){
reset(this.list);
}
}
通配符参数
@RunWith(MockitoJUnitRunner.class)
public class WildcardArgumentMatherTest {
public class SimpleService {
public int method1(int i, String s, Collection<?> c, Serializable ss){
throw new RuntimeException();
}
public void method2(int i, String s, Collection<?> c, Serializable ss){
throw new RuntimeException();
}
}
@Mock
private SimpleService simpleService;
@Test
public void wildcardMethod1(){
when(simpleService.method1(anyInt(),anyString(),anyCollection(),isA(Serializable.class))).thenReturn(100);
int result = simpleService.method1(1, "alex", Collections.emptyList(),"Mockito");
assertThat(result, equalTo(100));
}
@Test
public void wildcarMethod1WithSpec(){
when(simpleService.method1(anyInt(), eq("alex") ,anyCollection(),isA(Serializable.class))).thenReturn(100);
when(simpleService.method1(anyInt(), eq("mock") ,anyCollection(),isA(Serializable.class))).thenReturn(200);
int result = simpleService.method1(1, "alex", Collections.emptyList(),"Mockito");
assertThat(result, equalTo(100));
result = simpleService.method1(1, "mock", Collections.emptyList(),"Mockito");
assertThat(result, equalTo(200));
result = simpleService.method1(1, "test", Collections.emptyList(),"Mockito");
assertThat(result, equalTo(0));
}
@Test
public void wildcardMethod2(){
doNothing().when(simpleService).method2(anyInt(),anyString(),anyCollection(),isA(Serializable.class));
simpleService.method2(1, "alex", Collections.emptyList(),"Mockito");
verify(simpleService, times(1)).method2(1, "alex", Collections.emptyList(),"Mockito");
}
@After
public void destroy(){
reset(simpleService);
}
}
Assert Matcher 使用
import org.junit.Test;
import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
public class AssertMatherTest {
@Test
public void test(){
int i = 10;
assertThat(i, equalTo(10));
assertThat(i, not(equalTo(20)));
assertThat(i, is(10));
assertThat(i, is(not(20)));
}
@Test
public void test2(){
double price = 23.45;
assertThat(price, either(equalTo(23.45)).or(equalTo(23.53)));
assertThat(price,both(equalTo(23.45)).and(not(23.41)));
assertThat(price, anyOf(is(23.45),is(32.1)));
assertThat(price, allOf(is(23.45),not(is(32.1))));
assertThat(Stream.of(1,2,3).anyMatch(i->i>2),equalTo(true));
assertThat(Stream.of(1,2,3).allMatch(i->i>0),equalTo(true));
}
}
自定义Matcher类型
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Factory;
public class GreaterThan<T extends Number> extends BaseMatcher<T> {
private final T value;
public GreaterThan(T value){
this.value = value;
}
@Override
public boolean matches(Object actual) {
Class<?> clazz = actual.getClass();
if(clazz == Integer.class){
return (Integer)actual > (Integer) value;
} else if(clazz == Short.class){
return (Short)actual > (Short) value;
}else if(clazz == Long.class){
return (Long)actual > (Long) value;
} else {
throw new AssertionError("The number type " + clazz + " not supported");
}
}
@Factory
public static <T extends Number> GreaterThan<T> gt(T value){
return new GreaterThan<>(value);
}
@Override
public void describeTo(Description description) {
description.appendText("Compare two number failed");
}
}