概述
- 序言
- Fake的思想
- DoAnswer的用法
- 总结
- 参考资料
序言
对于mock我们已经在前面一篇文章里面详细介绍了。那篇文章里面介绍的方式可以解决我们大部分的问题,但是,是的,又是但是。有些中间过程,中间变量的verify,需要我们考虑更多。对于简单类型,mockito和powermock已经提供了verify方法,这里的重点在于不是简单类型,我们关注的是对象。对于中间变量是对象的verify,简单的verify函数力有未逮。对于这种情况,我们可以使用doAnswer或者Fake的思想。
Fake的思想
对于fake这种思想,我用下面的例子来表达。
A a;
........
a.method(Obj)
使用Fake的思想,我们会把A抽象成interface,然后真实的场景中,A的一个实例调用method去做事情,而我们需要对于Obj这个中间变量进行验证,这个时候我们需要拿到Obj的值。通常的做法就是我们自己实现一个A的实例,然后实现method方法,这个方法里面只是简单的把变量Obj的值存储下来,在后面的验证中取出来使用。
这里把A抽象成interface是一种隔离的思想,因为a.method也许是DB操作,或者是连接打印机等等行为,我们希望a.method不影响我们对于上面逻辑的验证,所以我们把它抽成interface,抽象出来。在我们fake之后,如果我们的测试没有问题,但是整个功能比如DB操作,或者是打印问题,我们就可以确认是a.method的问题了。
下面我们来看看具体的实现。
-------------- PersonPrinter.java ---------------
package com.mock;
public interface PersonPrinter {
void printPerson(HighSchoolStudent highSchoolStudent);
}
-------------- Person.java ---------------
package com.mock;
public interface Person {
String getName();
}
-------------- HighSchoolStudent.java ---------------
package com.mock;
public class HighSchoolStudent implements Person{
private String name;
private int age;
private int id;
private int grade;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public int getAge() {
return age;
}
@Override
public String getName() {
return name;
}
public void setGrade(int grade) {
this.grade = grade;
}
public int getGrade() {
return grade;
}
}
---------------- Student.java---------------
package com.mock;
public class Student {
private PersonPrinter personPrinter;
public void doAnswerHighSchoolStudent(){
HighSchoolStudent highSchoolStudent = new HighSchoolStudent();
highSchoolStudent.setName("highSchoolStudent");
highSchoolStudent.setAge(10);
highSchoolStudent.setId(123456789);
//verify highSchoolStudent
personPrinter.printPerson(highSchoolStudent);
highSchoolStudent.setGrade(3);
}
public void setPersonPrinter(PersonPrinter personPrinter) {
this.personPrinter = personPrinter;
}
}
我们希望verify中间结果highSchoolStudent是否正确,所以我们要拿到highSchoolStudent这个中间变量。
现在personPrinter.printPerson(highSchoolStudent); personPrinter是interface,所以根据我们fake的思想,我们不要抽象成interface了,只需要实现PersonPrinter这个interface就好。
------------------ FakePersonPrinter.java --------------
package com.mock;
public class FakePersonPrinter implements PersonPrinter {
private HighSchoolStudent highSchoolStudent;
@Override
public void printPerson(HighSchoolStudent highSchoolStudent) {
this.highSchoolStudent = highSchoolStudent;
}
public HighSchoolStudent getHighSchoolStudent() {
return highSchoolStudent;
}
}
这个fake类里面,我们只是把中间变量highSchoolStudent存储下来,当我们测试的时候,我们就可以很容易拿到它了。
@Test
public void testFakeObject() {
Student student = new Student();
PersonPrinter personPrinter = new FakePersonPrinter();
//注入fake object
student.setPersonPrinter(personPrinter);
//调用要测试的方法
student.doAnswerHighSchoolStudent();
//从fake对象里面取出中间变量
HighSchoolStudent highSchoolStudent = ((FakePersonPrinter) personPrinter).getHighSchoolStudent();
assertThat(highSchoolStudent.getId(), is(123456789));
assertThat(highSchoolStudent.getAge(), is(10));
assertThat(highSchoolStudent.getName(), is("highSchoolStudent"));
assertThat(highSchoolStudent.getGrade(), is(3));
}
通过上面的方式,我们就可以测试一些中间过程的逻辑了。上面的测试没有使用mock的库,mockito和powermock已经提供了上述过程的verify。那就是我们将要提到的doAnswer。
DoAnswer的用法
Student和PersonPrinter仍然保持不变,下面我们用doAnswer的方式,实现fake类似的作用。
首先我们需要实现powermock的Answer这个interface。
---------------- PersonPrinterAnswer.java ------------------
package com.mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class PersonPrinterAnswer implements Answer<Object> {
private Person person;
@Override
public Person answer(InvocationOnMock invocationOnMock) {
Object[] args = invocationOnMock.getArguments();
person = (Person) args[0];
return person;
}
public Person getPerson() {
return person;
}
}
我们需要mock PersonPrinter这个interface,当
personPrinter.printPerson(highSchoolStudent);
的时候我们就返回这个PersonPrinterAnswer。而answer()函数的参数就是personPrinter.printPerson()函数传进来的参数。通过这种方式我们就可以拿到中间变量highSchoolStudent。这里我同样会把中间变量记录下来,以供后面的过程使用。
在test里面我们只需要使用doAnswer就可以了。
@Test
public void testInterfaceDoAnswer() {
//mock一下PersonPrinter
PersonPrinter personPrinter = spy(PersonPrinter.class);
PersonPrinterAnswer personPrinterAnswer = new PersonPrinterAnswer();
//使用我们定义的personPrinterAnswer
doAnswer(personPrinterAnswer).when(personPrinter).printPerson(anyObject());
Student student = new Student();
//把我们mock的personPrinter注入student
student.setPersonPrinter(personPrinter);
//要测试的方法
student.doAnswerHighSchoolStudent();
//取出我们存在answer中的中间变量
HighSchoolStudent highSchoolStudent = (HighSchoolStudent) personPrinterAnswer.getPerson();
assertThat(highSchoolStudent.getId(), is(123456789));
assertThat(highSchoolStudent.getAge(), is(10));
assertThat(highSchoolStudent.getName(), is("highSchoolStudent"));
assertThat(highSchoolStudent.getGrade(), is(3));
}
通过使用Answer,我们verify了真正的临时对象的逻辑。对于不是interface的调用,fake就不是很方便了,但是Answer是可以达到目的的。这里我想要多举一个Answer用于static method的例子。
--------------- CommonPrinter.java --------------
package com.mock;
public class CommonPrinter {
public static void printPerson(Person person) {
}
}
--------------- Student.java --------------
package com.mock;
public class Student{
private PersonPrinter personPrinter;
public void doAnswerHighSchoolStudent(){
HighSchoolStudent highSchoolStudent = new HighSchoolStudent();
highSchoolStudent.setName("highSchoolStudent");
highSchoolStudent.setAge(10);
highSchoolStudent.setId(123456789);
personPrinter.printPerson(highSchoolStudent);
highSchoolStudent.setGrade(3);
}
public void callStaticDoAnswerHighSchoolStudent(){
HighSchoolStudent highSchoolStudent = new HighSchoolStudent();
highSchoolStudent.setName("highSchoolStudent");
highSchoolStudent.setAge(10);
highSchoolStudent.setId(123456789);
//调用static方法
CommonPrinter.printPerson(highSchoolStudent);
highSchoolStudent.setGrade(3);
}
public void setPersonPrinter(PersonPrinter personPrinter) {
this.personPrinter = personPrinter;
}
}
我在student里面增加了一个调用static方法的方法
callStaticDoAnswerHighSchoolStudent。在这个方法里面,我调用了static方法
CommonPrinter.printPerson(highSchoolStudent); 下面我们来看看怎么使用Answer保存这种方法的参数。
@Test
public void testStaticMethodDoAnswer() throws Exception {
//mock CommonPrinter
mockStatic(CommonPrinter.class);
//Answer没有变化
PersonPrinterAnswer personPrinterAnswer = new PersonPrinterAnswer();
//static方法的doAnswer的使用方式
PowerMockito.doAnswer(personPrinterAnswer).when(CommonPrinter.class, "printPerson", anyObject());
Student student = new Student();
//调用测试方法
student.callStaticDoAnswerHighSchoolStudent();
//取出存储的中间变量
HighSchoolStudent highSchoolStudent = (HighSchoolStudent) personPrinterAnswer.getPerson();
assertThat(highSchoolStudent.getId(), is(123456789));
assertThat(highSchoolStudent.getAge(), is(10));
assertThat(highSchoolStudent.getName(), is("highSchoolStudent"));
assertThat(highSchoolStudent.getGrade(), is(3));
}
我们的PersonPrinterAnswer并没有改变,这是因为我们测试的方法printPerson的参数没有什么变化,所以我们不需要改变它。
总结
本篇文章主要介绍了怎么测试中间变量。我们可以使用Fake的方式,但是Fake的方式局限性比较大。这个时候,我们就可以选择使用doAnswer的方式。我们这些方法都是为了打通加测试的路,哪种比较简单,以理解,你就可以选择哪一种。
参考资料
[1] 修改代码的艺术/(Michael C. Features)著;刘未鹏译.——北京:人民邮电出版社,2017.11
[2] https://blog.csdn.net/westkingwy/article/details/7485213
最后
以上就是热情口红为你收集整理的mockito和powermock的doAnswer使用以及Fake思想序言Fake的思想DoAnswer的用法总结参考资料的全部内容,希望文章能够帮你解决mockito和powermock的doAnswer使用以及Fake思想序言Fake的思想DoAnswer的用法总结参考资料所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复