how to learn unit test mock framework

Keywords: Spring Google Attribute Java

refer to http://www.strolling.cn/2017/01/how_to_learn_unit_test_mock_framework/

Every time we use various kinds of Mock framework for unit testing, we feel dizzy because we don't write enough. When we learn it, a new framework is popular. We think about why we can't always remember it and why we can't always say that we have mastered it. We think it's because every time we use it, it's scattered and unsystematic. So we write down this record and summarize it. On the one hand, it can help us to write CASE according to this sequence when we are learning a unit testing mock framework. On the other hand, when using mockito/powermock, we can copy code directly according to the scene.

Undoubtedly (1) - Make "false" targets

Since it is the use of Mock in unit testing, the first step is to learn how to create a false object, and then follow-up work is actually around this false object.

According to the different application scenarios, it can be divided into two ways:

(1) Mock: Make a complete dummy object;

Depending on the strategy, different false objects with default behavior can be defined, such as:

All methods return null without any real calls.

Apple apple= Mockito.mock(Apple.class);

All method calls are real calls

Apple apple= Mockito.mock(Apple.class,Mockito.CALLS_REAL_METHODS);

(2) Spy: Make a dummy object, but based on an existing real object.

The requirement is that most methods want to be invoked with real instances, and only some methods within the instance are customized.

Apple apple = new Apple();
Apple spiedApple= Mockito.spy(apple);

Undoubtedly (2) - Binding "false" objects

The first step is to get the false object, it will not be used automatically, otherwise, how to test it without mock? So the second step is to make use of your Mock object, that is, to bind the target under test and the mocked object, and to think about how one class uses another class:

1 The target under test is not created by itself, but the way the user passes it in:

(a) Pass in directly as a constructor parameter;
(b) Pass in using Set system method;

2 Target under test is responsible for creating

Essentially, creating is a new process (PS: except for static classes), so mock drops new and lets new return to the object to be mocked, so we can do everything. But from the point of view of the target to be tested, it can not be all new. Maybe this new is still a certain distance from the target to be tested: for example, using factory classes, using spring's @autowire, etc., so there are several cases from the code level. :

(1) Apple apple = new Apple();
(2) apple = AppleFactory.getIntance();
(3)
@Autowired
private Apple apple;

(a) For direct new: let new come up with an instance where all objects return mock

class AppleTree{

private Apple apple= new Apple();

}
@RunWith(PowerMockRunner.class)
@PrepareForTest({AppleTree.class}) //don't miss this statement and pay more attention it is caller for Apple, not Apple.
public class TestNewObject { 

@Test
public void test() throws Exception {
Apple apple= Mockito.mock(Apple.class);
PowerMockito.whenNew(Apple.class).withNoArguments().thenReturn(apple);
}

(b) Create mock creation methods for using other classes

It's usually a static factory situation. If it's not a "static factory" created by a common method of another instance, you need the mock instance.
In general, mockito does not support mocks for static classes temporarily in the face of static factory methods, so it needs to combine powermock:

@RunWith(PowerMockRunner.class)
@PrepareForTest({AppleFactory.class}) //don't miss this statement
public class TestStaticMethod {

@Test
public void test() throws Exception {
Apple apple= Mockito.mock(Apple.class);
PowerMockito.mockStatic(AppleFactory.class);
PowerMockito.when(AppleFactory.getInstance())
.thenReturn(apple);
}

(c) There is another case where frameworks are automatically created, such as Spring's @Autowired
JAVA reflection can be used to set it directly at this point, but since the mock tool is used, standard points can also be used, such as:

Apple apple= Mockito.mock(Apple.class);
Whitebox.setInternalState(testAppleInstance, "apple", apple);

Undoubtedly (3) - Articles on Mock Objects - Forgery

After learning the first two steps, you can begin to think about work. Now that you have a fake mock object, it is impossible not to do some "fake actions": match the previous method, and then make an action:

There are two kinds of matching:

Rough matching:

Mockito.when(mockedApple.getOwner(Mockito.anyString()).thenReturn("tom");

Accurate matching:

Mockito.when(mockedApple.getOwner(Mockito.eq("somegstring"))).thenReturn("tom");

Behavior includes the following three types:

(1) Non-real calls to defining methods;

Set its return value:

Mockito.when(mockedApple.getOwner()).thenReturn("tom");

Set its throw exception:

Mockito.when(mockedApple.getOwner()).thenThrow(new RuntimeException());

(2) Define methods to make real calls:

Mockito.when(mockedApple.getNumbers()).thenCallRealMethod();

(3) Adaptive change:

For example, setting different returns each time can be used:

 when(mockedApple.getOwner())
  .thenReturn("one")  //First act
  .thenCallRealMethod() //Second act
  .thenThrow(new RuntimeException()); //Third act

All other forms of advanced practices are not considered.

Undoubtedly (4) - Writing on Mock Objects - Verification Behavior

We can write verification points without considering the case itself. Sometimes we need to verify the behavior on the mocked object to verify the success of the case.
(1) Verify whether or not the call is invoked or the number of times it is invoked

 Mockito.verify(mockedApple, Mockito.times(2)).someMethod(Mockito.anyString());
 Mockito.verify(mockedApple, Mockito.never()).someMethod(Mockito.anyString());

(2) Verify call time

 Mockito.verify(mockedApple, Mockito.timeout(10)).someMethod(Mockito.anyString());

(3) Validate the call parameter values

Mode 1: Matcher - Direct validation of parameters

Simple Check:

 Mockito.verify(mockedApple, times(2)).someMethod(Mockito.eq("expectedString")); //mockitoRequire not to write directly here"expectedString"

Automatic meaning checking method:

Use Mockito.argThat+ArgumentMatcher(Matchers.argThat(Matcher matcher)
):

Mockito.verify(mockedApple).someMethod(Mockito.argThat(new ArgumentMatcher<String>(){
            @Override
            public boolean matches(String argument) {
                return argument.equals("expectedString");
}}));

Mode 2: Captor - Captures the parameters, and then verifies them

Use ArgumentCaptor to capture parameters and then process them further

ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(mockedApple).someMethod(argument.capture());
String value = argument.getValue();
Assert.assertEqual(value, expectedString);

Difference:
Also, sometimes ArgumentCaptor may be a better fit than custom matcher. For example, if custom argument matcher is not likely to be reused or you just need it to assert on argument values to complete verification of behavior.

(4) Verify the calling order

There are mainly two kinds, one is the method invocation sequence of the same mock object, the other is the method invocation sequence validation across the mock object. Refer to two examples respectively:

InOrder inOrder = Mockito.inOrder(mockedApple);
inOrder.verify(mockedApple).firstMethodCallName();
inOrder.verify(mockedApple).secondMethodCallName();
InOrder inOrder = Mockito.inOrder(mockedApple,mockedOrange);
inOrder.verify(mockedApple).methodCallName();
inOrder.verify(mockedOrange).methodCallName();

For various validations, sometimes reset mock objects are needed to handle issues such as sharing, and Mockito.reset() can be used.

Conclusion:

For a new unit testing framework, it is important to understand several things: "forgery object-binding object-custom object action-validation". The key point is that when mock/spy it then when customized match one method does something and verify after executed, it can also follow four steps. In addition, the above demonstrations are the basic points, the other are various forms of variants or advanced usage, and each framework has its own special requirements, must be complied with.

Posted by neroag on Thu, 10 Jan 2019 22:06:10 -0800