Mock
Up first throw a timing diagram, later when looking at the source code can be combined with this diagram, easier to understand.
I didn’t use annotations for debugging purposes
Whether you use Mockito.mock()
or PowerMockito.mock()
, it will call the MockitoCore.mock method (note here that the MockitoCore object is a Static property, so there is only one globally).
Once inside the MockitoCore#mock method, it does three main things, as shown below.
- performs a series of checks on the MockSettings passed in from the previous step
- build a proxy instance of the incoming class
- call the mockingStarted method
The core of the createMock method is the first two lines of code. As shown in the following figure.
Here the MockHandler object is first created, which is itself an interface. By tracing the MockHandlerFactory#createMockHandler method, you can see that the delegate pattern is used here and the real core logic is handled by MockHandlerImpl.
Moving on to the second line of MockMaker, which is also an interface, a static instance is obtained by way of Plugins.getMockMaker()
. By tracing the code, it was determined that the instance here is ByteBuddyMockMaker.
You can think of the framework as helping us dynamically create a class and load it into the JVM while the program is running, and I’ll cover that framework separately sometime later.
After entering the class, we find that it still uses delegates and actually handles SubclassByteBuddyMockMaker, so we can just look at the createMock method of the class directly.
In this method, three main things are done: 1.
- create a proxy class for the class you want to mock
- instantiate the proxy class and get the object
- store the previously created MockHandler into the proxy class
With these lines of code, we can get some information.
- The proxy class created by the createMockType() method must implement the MockAccess interface, otherwise the following force would report an error directly.
- The MockAccess interface must have a member variable named mockitoInterceptor, of type MockMethodInterceptor.
- The proxy class must be able to force a transition back to the class that was originally mocked (as shown in the following image)
Let’s look at the createMockType() method first, as shown below, which is again delegated, TypeCachingBytecodeGenerator -> SubclassBytecodeGenerator.
First look at TypeCachingBytecodeGenerator, as the class involves bytebuddy, will not take you to continue to follow, directly explain the role of the class.
Save a global TypeCache cache, if the class is currently mocked, once mocked, then directly from the cache will return, otherwise the execution of the callback method to create and insert the cache, this callback method is the implementation of SubclassBytecodeGenerator.
Write a UT to verify this, as shown below, and see that performing multiple mocks for the same class returns the same class.
Here’s a look at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator#mockClass, a method that implements how to create a proxy class. There is a lot of code in this method, but the core piece of code, which I have marked up for you, is as follows.
The above code shows how to create a proxy class bytebuddy.
① The subClass and implement methods declare that the proxy class needs to inherit and implement the parent class and interface of the mock class. This is the only way to force the transition back.
② The name method specifies the class name of the proxy class, and the rules for generating it are as follows.
③ mehod method parameter requires an ElementMatcher, which can be interpreted as a matcher, and after tracing the source code, we found that the value is any(), i.e. match all methods. The dispatcher method, which is immediately followed, specifies an interceptor for the above matched methods, and the matched methods will be intercepted by the interceptor, and the implementation class here is DispatcherDefaultingToRealMethod.
④ The defineField method indicates that a field is added to this proxy class, and this line of code is equivalent to
|
|
The implement method adds the MockAccess interface to the proxy class, which is completely tailored to the mockitoInterceptor.
This completes the construction of the proxy class, so let’s go back to SubclassByteBuddyMockMaker#createMock, the second thing this method does is instantiate. Since it is not very important here, we will not show the details with the code, and directly tell you that its implementation class is ObjenesisInstantiator, as shown in the following figure. Here the objenesis framework is used, which helps us to create instances of classes in a simple way.
After completing the instantiation, following the code all the way back to MockitoCore#mock, let’s look at this line of code.
|
|
The mockingProgress()
method returns a MockingProgress instance, MockingProgressImpl. Note that it is stored in the TreadLocal, meaning that each thread’s MockingProgress is the same.
Inside the mockingStarted
method, it looks like a listener pattern, which is not related to our main process, so we won’t study it.
Now that we have seen this class, let’s take a look at the methods of the MockingProgress interface, as shown below.
Spy
In the Mockito class, the difference between spy and mock is that spy has additional configuration for spiedInstance and defaultAnswer for the MockSettings parameter.
See the MockUtil#createMock method for more information on the role of spiedInstance, which is determined after the mock is completed. In short, it copies the properties of the spiedInstance to the mocked object.
Therefore, the following code gives two Orders with the same properties.
Regarding defaultAnswer, you can see that it specifies CALLS_REAL_METHODS, while for mock, this value is the default RETURNS_DEFAULTS.
Guessing that this parameter will make a difference in the actual execution, wait until the next section to explain.