mockito和powermock的doAnswer使用-第二篇

序言

我的上一篇文章mockito和powermock的doAnswer使用以及Fake思想,在那里,我介绍了Fake的基本思想,以及doAnswer的用法。但是,这里有个问题,上面文章里面我实现的answer是一个固定了类型的,如果这样的话,每种类型我们都需要构造这样的answer,这是没有必要的,这次我就在上次的基础上构造通用的answer类。

通用的Answer

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;
    }
}

这是我们上次的Answer实现方式。这个实现方式存在两个问题。1.类型固定为了Person,2.answer方法的返回值是不正确的,上面被我写成了person, answer的返回值应该是你mock的那个方法的返回值。这里mock的方法就是:

public static void printPerson(Person person)

那么,我们就从这两方面对上面的实现方式进行改进。

package com.mock;

import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import java.util.Arrays;
import java.util.List;

public class BaseMockAnswer implements Answer<Object> {
    private List<Object> arguments;
    private Object return4Answer;

    @Override
    public Object answer(InvocationOnMock invocationOnMock) {
        arguments = Arrays.asList(invocationOnMock.getArguments());
        return return4Answer;
    }

    public List<Object> getArguments() {
        return arguments;
    }

    public void setReturn4Answer(Object return4Answer) {
        this.return4Answer = return4Answer;
    }
}

针对第一个问题,我们直接返回了整个arguments,这样的话,无论我们需要监听的方法有多少参数,我们都可以接收到。而在test里面我们根据需要提取指定的参数,这样参数就动态化了,我们就解决了第一个问题。

针对第二个问题,我们在answer里面增加了一个属性return4Answer,这个属性就用于存储你mock方法的返回值。如果你mock的方法需要返回值,你可以提前把返回值构造出来,通过调用setReturn4Answer方法,把这个值传入answer中,等你mock的方法被调用到时,这个返回值就可以产生作用了。这样,我们解决了返回值的问题。

下面是改进之后,我们test的变化:

 @Test
    public void testStaticBaseMockAnswer() throws Exception {
        mockStatic(CommonPrinter.class);
        //声明Answer
        BaseMockAnswer baseMockAnswer = new BaseMockAnswer();
        PowerMockito.doAnswer(baseMockAnswer).when(CommonPrinter.class, "printPerson", anyObject());
        Student student = new Student();

        student.callStaticDoAnswerHighSchoolStudent();

        //从Answer中取出我们需要的参数
        HighSchoolStudent highSchoolStudent = (HighSchoolStudent) baseMockAnswer.getArguments().get(0);
        assertThat(highSchoolStudent.getId(), is(123456789));
        assertThat(highSchoolStudent.getAge(), is(10));
        assertThat(highSchoolStudent.getName(), is("highSchoolStudent"));
        assertThat(highSchoolStudent.getGrade(), is(3));
    }

    @Test
    public void testBaseMockAnswer() {
        PersonPrinter personPrinter = spy(PersonPrinter.class);
                //声明Answer
        BaseMockAnswer baseMockAnswer = new BaseMockAnswer();
        doAnswer(baseMockAnswer).when(personPrinter).printPerson(anyObject());
        Student student = new Student();
        student.setPersonPrinter(personPrinter);

        student.doAnswerHighSchoolStudent();

        //从Answer中取出我们需要的参数
        HighSchoolStudent highSchoolStudent = (HighSchoolStudent) baseMockAnswer.getArguments().get(0);
        assertThat(highSchoolStudent.getId(), is(123456789));
        assertThat(highSchoolStudent.getAge(), is(10));
        assertThat(highSchoolStudent.getName(), is("highSchoolStudent"));
        assertThat(highSchoolStudent.getGrade(), is(3));
    }

使用方式变化不大,这里我不关心返回值,所以我就没有构造返回值并存储进Answer,取出的arguments是一个list,而我则取出了第一个参数进行验证。

总结

我们实现程序的时候,通常没有办法第一遍就写的比较完美和成熟,我们需要不断的打磨,而我们的思维和编程技巧,正是在这样的打磨过程中不断进步的。我这次的改进,也是因为在真实的项目中遇到了上面提到的问题,产生了重复的代码。在消除重复代码的过程中,产生了上面的改进。如果我们遇到问题,不选择无视,而是迎难而上,我们也许会有不同的收获。

猜你喜欢

转载自blog.csdn.net/qisibajie/article/details/80547207