Writing method of unit test (JUnit 5)

Writing method of unit test (JUnit 5)

1 unit test concept

During unit test, it refers to the test code written for the smallest functional unit to determine that the actual result is consistent with the expected result.
The smallest functional unit in java is the method. When unit testing the function of a method, you don't have to care about the specific implementation logic of the method, as long as the result is consistent with the expectation. Method. If you pass the unit test, it also means that the change is successful. Unit tests can use mock to replace the strong dependency of business code logic.

2 unit test rules

Unit test rules

3 unit test writing method

3.1 about mockito

mockito can both mock interfaces and mock entity classes.

@What is the use of the RunWith annotation

This annotation modifies the junit execution class to SpringRunner, which is the alias of the test class SpringJunit4ClassRunner in the spring framework

Use @ RunWith(SpringJunit4ClassRunner.class) or @ RunWith(SpringRunner.class)

@RunWith is a runner, @ RunWith(SpringJUnit4ClassRunner.class) refers to running tests in the Spring test environment.

3.2 about @ Mock

For the tested service, the bean s injected through @ Resource or @ autowritten are mocked with @ mock annotation

@Mock will mask the whole object and replace all the methods in it with fake ones

The @ Spy annotation will only mock those methods we have hidden through methods such as when(), while other methods will still call their original real methods.

To use annotation, simply add @ Mock to the object to Mock

import com.jingpin.at.dao.OaMapper;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

import java.util.LinkedList;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;

class OaServiceImplTest {

    @Mock
    private OaMapper oaMapper;

    @Test
    void getOa() {
       //Do not annotate the object to mock
        LinkedList mockList = mock(LinkedList.class);
        //Mockito is a mocking framework that allows you to test with a concise API. Moreover, mockito is simple and easy to learn. It has strong readability and concise verification syntax
        //mock calls the element of the first mockList to return first, without adding an element to the mockList using add
        //This behavior is piling
        Mockito.when(mockList.get(0)).thenReturn("first");
        System.out.println(mockList.get(0));
        //The element of the second mockList called by mock returns an exception
        Mockito.when(mockList.get(1)).thenThrow(new RuntimeException());
        System.out.println(mockList.get(1));
        //null is returned for method calls that do not have a value
        System.out.println(mockList.get(2));
    }

    @Test
    void init() {
    }
}

3.3 parameter matcher

//Use anyInt() to match any int type parameter
Mockito.when(mockList.get(anyInt())).thenReturn("anything");
System.out.println(mockList.get(3));
//Use anyString() to match any String type parameter
Mockito.when(mockList.add(anyString())).thenReturn(true);
System.out.println(mockList.add("anything"));
//You can use your own defined matcher. argThat(isHaveOneElement()) is a custom parameter matcher
//Argthat (Matcher < T > Matcher) method is used to apply custom rules and can be passed into any implementation class that implements the Matcher interface
Mockito.when(mockList.addAll(argThat(new IsHaveOneElement()))).thenReturn(true);
System.out.println(mockList.addAll(new ArrayList(Collections.singleton("aa"))));

}

    //When customizing the parameter matcher, you need to inherit the ArgumentMatcher abstract class
    //It implements the Matcher interface of Hamcrest framework and defines the describeTo method
    //So we just need to implement the matches method and define rules in it
    class IsHaveOneElement implements ArgumentMatcher<List> {
        @Override
        public boolean matches(List argument) {
            return ((List) argument).size() == 1;
        }
    }

3.4 using assertions

Within the test method, assertions are very common. The actual information is compared with the expected results through the assertion method.

The main assertion methods are as follows:

//hhh the screenshot above is too long. There are too many cases with the same method name and different parameters
//Assert
//assertTrue(); The expected result is true
//assertFalse(); The expected result is false
//assertNotNull(); Expected result is not empty
int a = 1;
//assertEquals(); Expect two variables to be equal
assertEquals(a, equals(1));
//assertNotEquals(); Expect two variables to be unequal
assertNotEquals(a, 2);
//It is expected to end in 1 second, sleep for 500ms and run successfully
assertTimeoutPreemptively(Duration.of(1, ChronoUnit.SECONDS), () -> Thread.sleep(500));
//It is expected to end in 1 second, sleep for 500ms, and the operation fails
assertTimeoutPreemptively(Duration.of(1, ChronoUnit.SECONDS), () -> Thread.sleep(1200));

3.5 abnormal test

Exception handling is often required in the code. Junit provides assertThrows() to test whether the code throws the required exception. The first parameter is the specified exception class expected to be caught, and the second parameter encapsulates the method to be executed that will generate an exception. It can usually be used in combination with lambda expressions.

@Test
void testInvalidArgument() {
    assertThrows(InvalidArgumentException.class, () -> {
        ObjectName.testMethod("wrong");
    });
}

3.6 condition test

During unit testing, sometimes some @ Test methods do not need to be executed. In this case, some @ Test methods can be marked with @ Disabled. Why not write the Test of this method, but add @ Test, @ Disabled?

With @ Disabled, JUnit still recognizes that this is a test method, but it doesn't run for the time being.

Annotations like @ Disabled are called conditional tests. JUnit decides whether to run the current @ Test method according to different conditional annotations@ EnableOnOs is a condition Test judgment. The following example refers to running only on the mac system.

@Test
    @EnabledOnOs(OS.MAC)
    void testMac() {
        assertEquals(1, equals(1));
    }

3.7 verification method

3.7.1 whether it has been executed and the number of times it has been executed
mockList.add("once");
//The verify function performs verification once by default, and times(1) is usually omitted
verify(mockList).add("once");
verify(mockList, times(2)).add("once");
//Not executed
verify(mockList, never()).add("never");
//At least once
verify(mockList, atLeastOnce()).add("once");
mockList.add("twice");
mockList.add("twice");
//At least 2 times
verify(mockList, atLeast(2)).add("twice");
//Execute at most once
verify(mockList, atMostOnce()).add("once");
//Up to 2 times
verify(mockList, atMost(2)).add("twice");
List mock2 = mock(List.class);
List mock3 = mock(List.class);
//Verify whether mock2 and mock3 have interacted
verifyZeroInteractions(mock2,mock3);//Deprecated 
3.7.2 execution sequence
//Verify the function execution order of mock an object
mockList.add("first");
mockList.add("second");
//Verify that add first is executed before add second
InOrder inOrder = inOrder(mockList);
inOrder.verify(mockList).add("first");
inOrder.verify(mockList).add("second");
//Verify the function execution order of mock multiple objects
List list1 = mock(List.class);
List list2 = mock(List.class);
list1.add("first");
list2.add("second");
InOrder inOrder1 = inOrder(list1, list2);
inOrder1.verify(list1).add("first");
inOrder1.verify(list2).add("second");
3.7.3 have all methods for verifying mock objects been verified
mockList.add("one");
mockList.add("two");
//mockList calls add ("one") and add ("two") in turn
verify(mockList).add("one");
//verifyNoMoreInteractions(mockList);// Validation failed
//Verifynomore interactions means that there is no interaction with the list after the last verify
verify(mockList).add("two");
verifyNoMoreInteractions(mockList);//Validation succeeded
3.7.4 @Spy and spy

@The usage of Spy annotation is the same as that of @ mock annotation, except that when calling its method by the class annotated by @ Spy, the real method will be taken by default, and there is a return value@ Mock does not execute by default. If there is a return value, null is returned.

The usage of spy is the same as mock, and the real method is used by default

List list = new LinkedList();
//spyList is the monitoring object
List spyList = spy(list);
//piling
when(spyList.size()).thenReturn(100);
// Calling functions of real objects through spy objects
spyList.add("one");
spyList.add("two");
// Because the size() function is ignored, 100 is returned here
System.out.println(spyList.size());
List list1 = new LinkedList();

List spyList1 = spy(list);
//When spyList1 is empty, when().thenReturn() cannot be used. The subscript will be out of bounds
//when(spyList.get(0)).thenReturn("spyList");
//piling
doReturn("spyList").when(spyList).get(0);
System.out.println(spyList.get(0));

Tags: Java unit testing

Posted on Tue, 26 Oct 2021 19:08:49 -0400 by sbcwebs