Unit test mockito parameter matching usage scenarios and attention items

  Recently, I have just started to contact unit testing, and I have also used the mockito framework. When I first use it, I will encounter some questions and some misunderstandings. So here is a record of the problem of parameter matching using mockito.

1. Test scene

  First, let's take a look at the classes we want to test, which are mainly divided into Person information classes, which have age attributes; there is also a Salary salary class, which has Pseron attributes, and getSalary is judged based on the return value of the getData of the person And return the value of salary to see if you feel malicious , the whole class diagram is shown in the following figure:

mock-classes

  The first is the code of the Person class:

public class Person {
    
    
    private  int age;
    public int getAge() {
    
    
        return age;
    }
    public void setAge(int age) {
    
    
        this.age = age;
    }
    public int getData(Person person){
    
    
        return person.getAge()
    }
}

  Then the code of the Salary class:

public class Salary {
    
    
    private Person person;
    public int getSalary(int age){
    
    
        Person person=new Person();
        person.setAge(age);
        if(person.getData(person)<20){
    
    
            return 1000;
        }else if(person.getData(person)<40&&person.getData(person)>=20){
    
    
            return 5000;
        }else{
    
    
            return 500;
        }
    }
    public Person getPerson() {
    
    
        return person;
    }
    public void setPerson(Person person) {
    
    
        this.person = person;
    }
}

2. Introduction of external packages

  Because we will use the mockito framework, we introduce dependent packages in maven:

<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-all</artifactId>
	<version>1.10.19</version>
	<scope>test</scope>
</dependency>

3. Parameter matching test

  In the real environment, the method we call will depend on the methods of other classes, that is, other services, and of course database services. If you want to test the complete function during local unit testing, you will inevitably encounter these dependencies. Of course, we can configure these dependencies according to the real situation, but starting these services will cost a certain amount, which violates the original intention of the unit test design, so we need to use mockito to simulate these classes and simulate these services.

  So in this scenario, the Salary class relies on the service provided by Person, so you need to use the mock method to create the Person object:

Person person=mock(Person.class);

Of course, the creation of the Person class here can be done through the construction method, and then set through the setter and getter, but if the Person class has many or very complex fields, the mock method can help us in one step, let us focus on what we care about method.

  After simulating the object, we still need to simulate the expectation of the method, because we will use getDatathis method in the Person class. Here we simply return the value of age directly. The actual logic may be complicated, so we need to simulate Specify the return value when calling this method, use whenmethod:

when(person.getData(any())).thenReturn(19);

  This meaning is obvious. When we call the getDatamethod of the value of the mock object of person , it returns 19 when the parameter is any value, so correspondingly, when we call the Salary getSalarymethod, it will return 1000.

  getSalaryThe return value of the getDatamethod is related to the parameter person of the method, and the value of person is related getSalaryto the parameter age of the method. Can we getDatamatch the parameters of the person call instead of any()arbitrary values? Parameter matching here means that different returns are performed according to the value of the parameter when the method is called . The answer is yes, we need to create inherited ArgumentMact for parameter matching, we directly paste the code:

when(person.getData(argThat(new ArgumentMatcher<Person>() {
    
    
            @Override
            public boolean matches(Object o) {
    
    
                if (!(o instanceof Person)) {
    
      //判断实例很重要,如果没有这一步骤有的时候莫名其妙报nullpointer异常
                    return false;
                }
                return ((Person)o).getAge()<20;
            }
        }))).thenReturn(19);
  • The core of this is the implementation matchedmethod, and only the return truewill satisfy this match.
  • Remember to perform instance judgment first. If the age attribute of the passed parameter is less than 20, this match is satisfied.

  So if we want to test getSalary()all the branches, we need to construct a total of three parameter matching, the overall code is as follows:

when(person.getData(argThat(new ArgumentMatcher<Person>() {
    
    
            @Override
            public boolean matches(Object o) {
    
    
                if (!(o instanceof Person)) {
    
    
                    return false;
                }
                return ((Person)o).getAge()<20;
            }
        }))).thenReturn(19);
when(person.getData(argThat(new ArgumentMatcher<Person>() {
    
    
            @Override
            public boolean matches(Object o) {
    
    
                if (!(o instanceof Person)) {
    
    
                    return false;
                }
                return ((Person)o).getAge()>=20&&((Person)o).getAge()<40;
            }
        }))).thenReturn(35);
when(person.getData(argThat(new ArgumentMatcher<Person>() {
    
    
            @Override
            public boolean matches(Object o) {
    
    
                if (!(o instanceof Person)) {
    
    
                    return false;
                }
                return ((Person)o).getAge()>=40;
            }
        }))).thenReturn(60);

assertEquals(1000,salary.getSalary(10));
assertEquals(5000,salary.getSalary(30));
assertEquals(500,salary.getSalary(40));

4. Another simple test method

  getSalaryThis method currently only has three branches. If there are more branches, wouldn't it be necessary to construct a Matcher in all cases? So here is an optimization method that is to directly and decisively set a situation and then test the method, so that it can also have the same coverage as parameter matching. The code is as follows:

when(person.getData(any())).thenReturn(19);
assertEquals(1000,salary.getSalary(10));
when(person.getData(any())).thenReturn(35);
assertEquals(5000,salary.getSalary(30));
when(person.getData(any())).thenReturn(60);
assertEquals(500,salary.getSalary(40));

This simple test method can guarantee coverage, but it ignores the dependence between the incoming ageand getDatathe connection .

5. Summary

  The parameter matching function of mockito allows us to simulate the real situation as much as possible when we perform unit tests, but if the parameters are complex, then we need to write a lot of branches, so that the time to write unit tests is longer than the code itself, which is obviously It is wrong, so we need to measure which method to choose, but no matter which method we choose, we must ensure the corresponding coverage.

Guess you like

Origin blog.csdn.net/u012397189/article/details/98751254