preface

Before, in the development of writing unit test phase, we found that the methods to be tested contain dependencies: external interface RPC call, DB call. In some cases, partial dependencies are unstable or cannot be invoked in the test environment, causing use cases to occasionally fail to execute.

Another point is that many use cases are annotated with @springruntest at the beginning of the test case, which causes the whole Spring container to start when running the test case, making it very slow to run the test case. When running use cases on larger projects, and even getting to the point where it takes 5-6 minutes to start the container each time, I became a bit overwhelmed, anxious to change every line of code, because if I made a mistake, I had to wait another 5-6 minutes to see the effect. Later, I consulted my colleagues and searched on the Internet and found a relatively fast and safe solution. I used Mock framework –Mockito to learn and practice for a period of time and summarized the application methods.

Mockito

Mockito is currently the most popular Mock framework for unit testing.

What is the Mock

Mock mocks can be used to Mock out external dependencies in unit testing.

For objects (such as external services) that are not easy to construct or retrieve in a unit test, using a Mock object reduces the complexity of the test and only cares about the method of the current unit test.

Why Mock

The purpose of unit testing is to verify the correctness of a unit of code. All you really need to verify is the correctness of an output corresponding to an input. The introduction of external dependent services increases the complexity of the original unit and implicitly mixes the content of other functions into the unit.

Using Mock objects for unit testing, development can only care about the code that is testing the unit.

Use the sample

Take a look at the code example, assuming the following scenario:

  • Verify the interface for obtaining user information, including the user ID, user nickname, and WHETHER the user is VIP
  • Whether VIP needs to be obtained by external service VIPService through RPC call. In the test environment, poor machine performance or poor network will lead to unstable use cases
  • Write unit tests to determine whether the user VIP information is returned correctly

The requirement is to determine whether the format returned by the interface to obtain user information is correct. It has nothing to do with the return value of the VIP interface, as long as the field returned by the VIP interface is passed through. The test code is as follows:

@RunWith(PowerMockRunner.class)
public class UserServiceTest {

    @InjectMocks
    private UserService userService;

    @Mock
    private VIPService vipService;

    @Test
    public void getUserInfo(a) {
        Mockito.when(vipService.isVip(Mockito.anyString())).thenReturn(true);

        Result result = userService.getUserInfo("123");

        Assert.assertEquals(true, result.getData().get("isVip")); }}Copy the code

Explain some of the comments in the code above

Mock: Create a Mock

InjectMocks: Mock an instance into which the rest of the mocks created with Mock annotations will be injected.

Mockito.when(…) .thenReturn(…) The Mock method returns the result specified by thenReturn if the conditions in when are met.

In this code, an instance of VipService is created using the @Mock annotation, and UserService is created using @InjectMock. The VipService instance created by the Mock is injected into the UserService instance. The behavior of vipService can be simulated when writing test cases.

Mockito.when(vipService.isVip(Mockito.anyString())).thenReturn(true); This code indicates that the vipService.isVIP method will return true regardless of any parameters passed to it, so that normal testing of the interface for retrieving user information is not affected and the returned data can be validated using assertions.

The scene I encountered

These are the simplest examples of Mockito practices. There are a variety of requirements that need to be met in a production environment. Here are some of the scenarios I have encountered.

The mock abnormal

In this scenario, the method declares that it may throw an A exception, and there are many possibilities for an A exception. Different exceptions correspond to different messages. In order to verify the function of throwing an A exception, it is necessary to simulate the method to throw an A exception with the specified message.

To use it, you define a Rule attribute that sets the type of exception thrown and the message it carries. The brief code is as follows:

class AException extends RuntimeException {
    private final int code;

    public AException(int code, String msg) {
        super(msg);
        this.code = code; }}@RunWith(PowerMockRunner.class)
public class MockExceptionTest {

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void mockException(a) {

        thrown.expect(AException.class);
        thrown.expectMessage("expected message");

        // test code}}Copy the code

The mock empty methods

Mock an empty method by calling doNothing().when()… .

Mock static methods

To Mock a static method, first add a annotation to the beginning of the class: @prepareForTest ({classNamea.class}).

Before you need Mock class methods, add code: PowerMockito.mockStatic(classNamea.class); “And then you can Mock happily. The brief code is as follows:

class ClassNameA {

    public static int methodA(a) {
        // code

        returnret; }}@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassNameA.class})
public class MockStaticClassTest {

    @Test
    public void mockStaticMethod(a) {
        PowerMockito.mockStatic(ClassNameA.class);

        Mockito.when(ClassNameA.methodA()).thenReturn(1);
        // test code}}Copy the code

Part of the mock

For some scenarios, in a unit test, you need a method to Mock, a method to follow the normal logic, this operation will start the container, there is no suitable way to do this operation, if there is a better method please point out. My current approach is to break up the original method into smaller units where you can Mock each other and actually execute the entire code during integration tests.

These are the scenarios I encounter in daily development

conclusion

Unit test is a verification work for verifying the correctness of the smallest unit of code logic. Writing a good unit test is a very necessary work for finding code bugs, ensuring system stability and reconstruction, which can find some hidden problems in advance.

JUnit Best Practices This article mentions mocking all external services and states:

Mock out all external services and state Otherwise, behavior in those external services overlaps multiple tests, And state data means that different unit tests can influence each other’s outcome. You’ve been taken a wrong turn if you have to run your tests in a specific order, or if they only work when your database or network connection is active.

Also, this is important because you would not love to debug the test cases which are actually failing due to bugs in some external system.

So use mocks whenever possible for unit tests with external services.

Original article, writing is limited, talent and learning shallow, if the article is not straight, hope to inform.

If this article helped you, please give it a thumbs up. Thanks

More exciting content, please pay attention to the individual public number.

See JUnit Best Practices Guide