mock使用
mock主要在单元测试的时候用来模拟外部依赖接口的返回,即method stub的作用。 一般而言,在常见的单元测试的编写中,通过mock外部依赖来使得待测试的代码能往下执行。
在单测中,莫过于以下三个步骤,
- 确定目标
- 构造条件
- 验证
mock场景
- mock对象
- mock方法:对象方法、静态方法、私有方法,返回正常返回值或抛出异常
- mock私有属性
创建mock对象
- 被mock的目标对象
public class Target {
private String name = "default";
public String getName() {
return name;
}
public String someMethod(String arg) {
return arg + "!!!";
}
static String staticMethod() {
return "static";
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
sb.append("\"name\":\"")
.append(name).append('\"');
sb.append('}');
return sb.toString();
}
}
- 编码方式
Target target = BDDMockito.mock(Target.class);
- 注解方式
public class TestMockito {
@Mock
private Target target;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this); // 使用注解方式需要初始化或者在类上加上注解@RunWith(MockitoJUnitRunner.class)
}
@Test
public void testxxx(){
// ...
}
}
- 注入mock对象到其他对象中
@InjectMocks
private XXService xxService;
@Mock
private A a;
@Mock
private B b;
@InjectMocks将@Mock注解的对象注入到XXService对象中,注入的方式有三种:构造器注入、setter注入和field注入(优先级即为此顺序)。
Again, note that @InjectMocks will only inject mocks/spies created using the @Spy or @Mock annotation.
@InjectMocks也需和 MockitoAnnotations.initMocks或MockitoJUnitRunner一同使用。
stub方法
- stub来模拟方法返回,Mockito.when()
import static org.mockito.Mockito.*;
@Test
public void testIteratorStyleStubbing() {
// when(target.someMethod("a")).thenReturn("b");
when(target.someMethod(Mockito.anyString())).thenReturn("a").thenReturn("b");
System.out.println(target.someMethod("1")); // a
System.out.println(target.someMethod("1")); // b
System.out.println(target.someMethod("1")); // b
}
@Test
public void testIteratorStyleStubbing2() {
when(target.someMethod(anyString())).thenReturn("a", "b", "c", "d");
System.out.println(target.someMethod("1")); // a
System.out.println(target.someMethod("1")); // b
System.out.println(target.someMethod("1")); // c
System.out.println(target.someMethod("1")); // d
System.out.println(target.someMethod("1")); // d
}
@Test
public void testStubingWithCallback() {
when(target.someMethod(anyString())).thenAnswer(stubbing -> "callback with args:" + Arrays.asList(stubbing.getArguments()).toString
());
System.out.println(target.someMethod("a"));
}
@Test
public void doXX() {
doThrow(new RuntimeException()).when(target).someMethod(anyString());
try {
target.someMethod("a");
} catch (Exception e) {
Assert.assertEquals(e.getClass(), RuntimeException.class);
}
// doNothing()
// doReturn()
// doAnswer()
// doCallRealMethod()
}
- 静态方法和私有方法
因为Mockito是通过CGLIB继承目标类来达到代理的目的,所有无法重写静态方法和private方法,所以无法mock。
mock私有属性
通过反射的方式给attribute赋值,有两个工具类:
- spring的ReflectionTestUtils.setField()
- Mockito的FieldSetter
二者都是基于反射的field赋值。
@Test
public void mockPrivateAttr() {
Target injectTarget = new Target();
// ReflectionTestUtils.setField(injectTarget, "name", "mockName");
try {
FieldSetter.setField(injectTarget, Target.class.getDeclaredField("name"), "mockName");
System.out.println(injectTarget.getName());
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}