1.得到的对象同样可以进行“监管”,即验证和打桩。
2.如果不对spy对象的methodA打桩,那么调用spy对象的methodA时,会调用真实方法。
3.如果不对mock对象的methodA打桩,将doNothing,且返回默认值(null,0,false)。
1.同样可以对对象的特定public方法进行打桩。
2.Shadow可以对public static的方法进行打桩,spy不可以。
3.当测试对象为Shadow对象时,无法被统计覆盖率,spy对象可以被统计。
4.Shadow需要定义一个类,并且需要使用注解,spy不用。
5.无法对Shadow对象的public方法进行verify,spy对象可以。
6.Shadow对象不需要我们主动显式注入到测试程序中,因为测试程序在创建Shadow的目标对象后,会自动为目标对象打桩,而spy对象需要我们主动显式注入到测试程序中,否则桩无法生效,也无法verify。1.
public class TestSubject{
public void methodA(){
throw new RuntimeException();
}
public void methodB(){
System.out.println("methodB begin");
methodA();
methodC()
System.out.println("methodB end");
}
public void methodC(){
System.out.println("methodC");
}
}
public class Test{
//此用例中使用spy的原因是我要测试的是TestSubject的methodB方法,所以调用methodB时必须执行其
//真实的方法体,methodB会调用methodA,methodA会抛异常,所以要绕过methodA
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
//避免调用mehtodB时抛运行时异常。
doNothing().when(spyT).methodA();
sptT.methodB();
}
}
public class TestSubject{
public int methodA(){
//根据某成员变量的值去计算得出一个value,这个过程包含了复杂的逻辑和层层方法嵌套调用
return value;
}
public void methodB(){
int key = methodA();
switch(key){
case 0:
//do something
case 1:
//do something
case 2:
//do something
}
}
}
public class Test{
//此用例中使用spy的原因是我要测试的是TestSubject的methodB方法,所以需要
//调用真实对象的methodB,methodB的输入来自methodA的返回值。但是methodA的计算十分复杂,
//那么想要methodA返回你想要的值就不那么容易,别人看起来也不直观,不确定methodA否是真的
//返回0,1,2。那么就可以对methodA打桩,对真实对象打桩,就要用到spy.
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
//第一次,第二次,第三次调用methodA时,分别返回0,1,2
when(spyT.methodA()).thenReturn(0,1,2);
for(int i=0; i<=2; i++){
spyT.methodB();
}
//assert && verify
}
}
public class TestSubject{
public void methodA(){
System.out.println("methodA");
}
public void methodB(int i){
int key;
//根据参数i进行复杂运算,得出结果赋值给key
switch(key){
case 0:
methodA();
case 1:
methodC();
case 2:
methodD();
}
}
public void methodC(){
System.out.println("methodC");
}
public void methodD(){
System.out.println("methodD");
}
}
public class Test{
//此用例中使用spy的原因是我要测试的是TestSubject的methodB方法,所以需要
//调用真实对象的methodB,此例中需要verify输入特定的i,是否能分别走进case 0,1,2,
//methodA,C,D方法体内的东西都没法获取并证明methodA,C,D被调用过。那么就只能verify了,
//verify只能针对mock对象,其实spy对象,也可以使用verify
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
//假定输入1,能让key==0
spyT.methodB(1);
//assert && verify
verify(spyT).methodA();
}
}
public class TestSubject{
public void methodB(TestObject obj, i){
int key;
//这方法执行的内容非常必要,所以obj需要真实对象。
obj.doImportantThing();
//根据参数i进行复杂运算,得出结果赋值给key
switch(key){
case 0:
LayoutInflater inflater = obj.getLayoutInfalter();
ViewGroup v = inflater.inflate(R.layout.complex_layout,null,false);
v.setVisibility(View.GONE);
//do something can be verify
case 1:
methodC();
case 2:
methodD();
}
}
public void methodC(){
System.out.println("methodC");
}
public void methodD(){
System.out.println("methodD");
}
}
public class TestObject{
public void doImportantThing(){
//do something nessisary for TestSubject
}
//一个layout文件经常无法inflate出一个ViewGroup,所以很可能你需要该方法返回一个
//mock对象,然后你可以随心所欲指定inflate出来的ViewGroup对象
public LayoutInflater getLayoutInfalter(){
//obtain LayoutInflater
}
}
public class Test{
//此用例中使用spy的原因是我要测试的是TestSubject的methodB方法,所以需要
//调用真实对象的methodB,此例中需要verify输入1后,是否进入case 0;因为
//TestObject#doImportantThing()中的内容是必须执行的,所以TestObject需要传入的
//真实对象,但是R.layout.complex_layout太复杂了,里面都是厂商定制的控件,无法加载,
//进入case 0后,将无法正常跑下去,那么可以通过spy TestObject,然后对getLayoutInfalter
//打桩,使得返回一个mock LayoutInfalter,然后再对mock LayoutInfalter的inflate方法打桩,
//使得不去真正加载R.layout.complex_layout,而是返回一个自己创建好ViewGroup,使得代码
//能继续跑下去
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
TestObject obj = new TestObject();
TestObject spyObj = Mockito.spy(obj);
LayoutInflater mockInflater = mock(LayoutInflater.class);
ViewGroup mockViewGroup = mock(ViewGroup.class);
when(mockInflater).inflate(anyInt(), isNull(ViewGroup.class), anyBoolean()).thenReturn(mockViewGroup);
doReturn(mockInflater).when(spyObj).getLayoutInfalter();
//假定输入1,能让key==0
spyT.methodB(1);
//assert && verify
verify(mockViewGroup).setVisibility(View.GONE);
//verify other
}
}
总而言之,如果你想对一个真实对象的某个方法打桩( doReturn().when().method() ),verify真实对象的public方法( verify().method() ),绕过真实对象的某个public方法( doNothing().when().method() ),你可以使用spy后的对象,如:
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
特别需要注意的是,t和spyT是两个不同的对象,后面的代码必须要使用spyT,打桩才有效,才能verify TestSubject的方法。如果你只是spy(t),而后面的代码仍然调用t.methodB()的话,则打桩无效,无法verify。而要是保证调用的是spyT.methodB()。
public class TestSubject{
public void methodA(){
throw new RuntimeException();
}
public void methodB(){
System.out.println("methodB begin");
methodA();
methodC()
System.out.println("methodB end");
}
public void methodC(){
System.out.println("methodC");
}
public void mothodD(TestObject obj){
obj.doSomething();
}
}
public class TestObject{
public void doSomething(){
}
}
public class Test{
//错误用法1
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
//避免调用mehtodB时抛运行时异常。
doNothing().when(spyT).methodA();
t.methodB();//错误
//spyT.methodB();//正确
}
//错误用法2
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestSubject spyT = Mockito.spy(t);
//避免调用mehtodB时抛运行时异常。
doNothing().when(t).methodA();//错误
//doNothing().when(spyT).methodA();//正确
spyT.methodB();
}
//错误用法3
@Test
public void testMethodB(){
TestSubject t = new TestSubject();
TestObject obj = new TestObject();
TestObject spyObj = Mockito.spy(obj);
t.methodD(obj);//错误
//t.methodD(spyObj);//正确
//verify
verify(spyObj).doSomething();//正确
//verify(obj).doSomething();//错误
}
}