Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

unit testing - How does mockito when() invocation work?

Given the following Mockito statement:

when(mock.method()).thenReturn(someValue);

How does Mockito go about creating a proxying something for a mock, given that the mock.method() statement will pass the return value to when()? I imagine that this uses some CGLib stuff, but would be interested to know how this is technically done.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The short answer is that in your example, the result of mock.method() will be a type-appropriate empty value; mockito uses indirection via proxying, method interception, and a shared instance of the MockingProgress class in order to determine whether an invocation of a method on a mock is for stubbing or replay of an existing stubbed behavior rather than passing information about stubbing via the return value of a mocked method.

A mini-analysis in a couple of minutes looking at the mockito code is as follows. Note, this is a very rough description - there are a lot of details in play here. I suggest that you check out the source on github yourself.

First, when you mock a class using the mock method of the Mockito class, this is essentially what happens:

  1. Mockito.mock delegates to org.mockito.internal.MockitoCore.mock, passing the default mock settings as a parameter.
  2. MockitoCore.mock delegates to org.mockito.internal.util.MockUtil.createMock
  3. The MockUtil class uses the ClassPathLoader class to get an instance of MockMaker to use to create the mock. By default, the CgLibMockMaker class is used.
  4. CgLibMockMaker uses a class borrowed from JMock, ClassImposterizer that handles creating the mock. The key pieces of the 'mockito magic' used are the MethodInterceptor used to create the mock: the mockito MethodInterceptorFilter, and a chain of MockHandler instances, including an instance of MockHandlerImpl. The method interceptor passes invocations to MockHandlerImpl instance, which implements the business logic that should be applied when a method is invoked on a mock (ie, searching to see if an answer is recorded already, determining if the invocation represents a new stub, etc. The default state is that if a stub is not already registered for the method being invoked, a type-appropriate empty value is returned.

Now, let's look at the code in your example:

when(mock.method()).thenReturn(someValue)

Here is the order that this code will execute in:

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

The key to understanding what is going on is what happens when the method on the mock is invoked: the method interceptor is passed information about the method invocation, and delegates to its chain of MockHandler instances, which eventually delegate to MockHandlerImpl#handle. During MockHandlerImpl#handle, the mock handler creates an instance of OngoingStubbingImpl and passes it to the shared MockingProgress instance.

When the when method is invoked after the invocation of method(), it delegates to MockitoCore.when, which calls the stub() method of the same class. This method unpacks the ongoing stubbing from the shared MockingProgress instance that the mocked method() invocation wrote into, and returns it. Then thenReturn method is then called on the OngoingStubbing instance.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...