mock使用
mock主要在單元測試的時候用來模擬外部依賴接口的返回,即method stub的作用却妨。 一般而言饵逐,在常見的單元測試的編寫中,通過mock外部依賴來使得待測試的代碼能往下執(zhí)行彪标。
在單測中,莫過于以下三個步驟掷豺,
- 確定目標(biāo)
- 構(gòu)造條件
- 驗證
mock場景
- mock對象
- mock方法:對象方法捞烟、靜態(tài)方法、私有方法当船,返回正常返回值或拋出異常
- mock私有屬性
創(chuàng)建mock對象
- 被mock的目標(biāo)對象
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對象中题画,注入的方式有三種:構(gòu)造器注入、setter注入和field注入(優(yōu)先級即為此順序)德频。
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()
}
- 靜態(tài)方法和私有方法
因為Mockito是通過CGLIB繼承目標(biāo)類來達(dá)到代理的目的壹置,所有無法重寫靜態(tài)方法和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();
}
}