Here is the next guide on the Mockito. In it, on the one hand, I tried to describe the functionality of this library so that a reader unfamiliar with it immediately got the opportunity to fully use it, and not just a general idea about it. On the other hand, I wanted to make it compact enough and structured so that you can quickly read it completely and quickly find in it something once read, but forgotten. In general, this is such an article, which would be useful to me when I first encountered this library and did not really understand how it works.
I suppose that even now it may be useful to me - sometimes I forget one of these, and itβs more convenient to remember the material not according to official documentation or other people's articles, but according to my own, let's say, outline. At the same time, I tried to construct the text in such a way that it was convenient first of all to get acquainted with Mockito from scratch, and in some places I sort out the seemingly obvious things - not all of which were obvious to me from the very beginning.
In short, Mockito is a stub framework.
As you know, when testing code (first of all, unit testing, but not only), the element being tested often needs to provide instances of classes that it should use when working. At the same time, they often do not have to be fully functional - on the contrary, they are required to behave in a strictly predetermined manner, so that their behavior is simple and completely predictable. They are called stubs. To get them, you can create alternative test interface implementations, inherit the necessary classes with redefinition of the functional, and so on, but all this is rather inconvenient, redundant and fraught with errors. A more convenient solution in all senses is specialized frameworks for creating stubs. One of those (and perhaps best known for Java) is the Mockito.
Mockito allows you to create with one line of code a so-called mock (something like the basis for the right stub) of any class. For such a mock immediately after creation, some default behavior is typical (all methods return predefined values ββ- usually null
or 0
). You can redefine this behavior in the desired way, check with the necessary degree of detail in addressing them, and so on. As a result, the mock and becomes a stub with the desired properties. Below I will explain in detail how to do this.
I note that you can also create a mock for those classes whose new instance cannot be created as such, in particular, classes with exclusively private constructors such as singletons and utility classes, and with a minimal framework setting - and enumerations.
When writing this article I used:
For my inhuman experiments, I wrote this service interface, providing access to certain data.
public interface DataService { void saveData(List<String> dataToSave); String getDataById(String id); String getDataById(String id, Supplier<String> calculateIfAbsent); List<String> getData(); List<String> getDataListByIds(List<String> idList); List<String> getDataByRequest(DataSearchRequest request); }
And this (let it be for the order) is the class code of the request passed to the last of the interface methods.
@AllArgsConstructor @Getter class DataSearchRequest { String id; Date updatedBefore; int length; }
Data units are identified by ID and have some more characteristics, but directly in the form in which the service returns, they are strings, and not some more complex objects. I donβt miss anything important, and the examples are easier and clearer.
I note immediately: in the examples below, for clarity, I directly call the redefined methods of my mock objects, but with real testing, the idea is not at all in this! In a real test, I would consistently do the following:
DataService
), which I, in fact, would test;The central class of Mockito, through which it is supposed to access most of the functionality, is, in fact, a class called Mockito
(there is also a class BDDMockito
, which provides approximately the same capabilities in a form more suitable for BDD , but here I will not dwell on it) . Access to the functionality is implemented through its static methods.
To create a mock of the DataService
class, I just have to do the following:
DataService dataServiceMock = Mockito.mock(DataService.class);
Done - I got a copy of the class I needed. It will be accepted by any method or constructor that requires a parameter of this type (for example, the constructor of the class that I want to test). Even if further it is waited by check with predilection, it will pass it: not only the instanceof DataService
returns true
, but also the dataServiceMock.getClass()
- exactly DataService.class
. In some formal way, to programmatically distinguish a mock-object from an ordinary one turns out to be a rather difficult task, which is logical: after all, the first is designed just to be indistinguishable from the second. However, Mockito has a tool for this - the Mockito.mockingDetails
method. Passing an arbitrary object to it, I get an object of the MockingDetails
class. It contains information that this object is from the point of view of the Mockito: whether it is a mock, spy (see below), how it was used, how it was created, and so on.
I should especially mention the situation when I try to create a mock for a final class or a mock enum instance or override the behavior of a final method. In this case, when the behavior of the Mockito default code above refuses to work, referring to this circumstance. However, this can be changed - it is enough to create the file test/resources/mockito-extensions/org.mockito.plugins.MockMaker
in the project (with the standard device of the project directory tree) and enter the line:
mock-maker-inline
After that, you can simulate the usual way final classes and enums, as well as override final methods.
The mock I got in action is maximized: no method will have any impact on anything when called, and the return value will be null
for object types and 0
for primitive ones. Note: if the method returns a collection, the mock will default to returning not null
's, but empty instances of the collections. For example, for a List
this will be an empty LinkedList
regardless of what the real method should have returned. But as array values, primitive or object, I get null
. The default behavior (and not only it) can be changed with the help of the functional of the MockSettings
class, but this is rarely necessary.
Anyway, in most cases, I will not need the default behavior, and in the next section I will discuss in detail how to set what is required instead.
However, what if I want to use as a stub an object of a real class with the existing functionality, overriding the work of only part of its methods? If we are talking about unit testing, such a need usually (but not always) indicates that the design is not all right with the design, and in principle this is not recommended. However, there are situations when this cannot be avoided for some reason. In this case, the Mockito has a so-called spy, "spy". Unlike mocks, they can be created on the basis of both the class and the finished object:
DataService dataServiceSpy = Mockito.spy(DataService.class); // or DataService dataService = new DataService(); dataServiceSpy = Mockito.spy(dataService);
When creating a spy based on a class, if its type is an interface, a regular mock object will be created, and if the type is a class, then Mockito will try to create an instance using the default constructor (without parameters). And only if there is no such constructor, an error will occur and the test will not work.
The default behavior of spy objects is identical to the behavior of a regular class instance, but they give me the same features as mock objects: they allow you to redefine their behavior and observe how they are used (see the following sections). Important point: spy is not a wrapper around the instance on which it was created! Therefore, calling the spy method will not affect the state of the original instance.
So, how to make mock or spy do what I need. Further, I will write everywhere simply "mock" - this will mean "mock or spy", except when expressly stated otherwise.
In general, controlling the behavior of a mock object is reduced to one obvious concept: when a mock was influenced in this way (that is, a certain method was called up with such and such arguments), it must react in such and such a way. This concept has two implementations within the Mockito class - the main one recommended by developers for use wherever possible, and the alternative one used where the main one is not suitable.
The main implementation is based on the Mockito.when
method. This method accepts as a βparameterβ a call to the overridden method of a mock object (thus the defined impact is fixed) and returns an object of the OngoingStubbing
type, which allows you to call one of the methods of the Mockito.then...
family Mockito.then...
(this is how the response to this impact is set). All together in the simplest case looks like this:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data);
After this operation, I will dataService
the getAllData()
method on the getAllData()
object and get the object specified in the first line of the listing.
Here, the usual "object-oriented" intuition may give some glitch, so it is worthwhile to dwell on this in a bit more detail. From a Java syntax point of view, the value passed to the when
method as a parameter is, of course, the value returned by the override method. For mock, this is an empty value, for spy, the value returned by the real object method. But thanks to the magic of the "under the hood" Mockito magic, the when
method will work in a regular manner (and will not fall at startup with an error) only if inside the brackets after when
there is exactly a call to the mock-object method.
Such an ideology often acts when defining mock behavior in a Mockito: calling a method (mock object or Mockito
class), I try not to get the value returned by it, but to somehow influence the possible call of that method of the mock object I work with: specify its boundaries, set the result, set monitoring of its calls, and so on. It sounds somewhat vague, I admit, and at the first collision it looks strange, but, having figured it out, very soon you begin to feel this approach as completely natural in the context of working with stubs.
An alternative implementation of the binding condition and call result is the methods of the Mockito.do...
family Mockito.do...
These methods allow you to set the behavior starting with the result of the call and return an object of the Stubber
class, with which you can set a condition. The same binding as above performed in this way looks like this:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.doReturn(data).when(dataService).getData()
What is the difference, why is linking through Mockito.when
considered preferable and when does one have to use the methods of Mockito.do...
? Note: in the first implementation, when defining the behavior of a method (in this case, getAllData()
), the call to its not yet redefined version is first called, and only then, in the depths of the Mockito, the redefinition occurs. In the second, such a call does not occur - the Stubber.when
passed directly to the Stubber.when
method, and already the object of the same type returned by this method, but of a different nature, is Stubber.when
redefined method. This difference determines everything. Linking through Mockito.do...
does not control at any time at the compilation stage which method to override which I call and whether it is compatible with the specified return value. Therefore, it is usually preferable to Mockito.when
- there can be no errors with it. But there are cases when I want to avoid calling an unredefined method β for a newly created mock, such a call is quite acceptable, but if I have already redefined this method or deal with spy, it may be undesirable, and when throwing an exception, it will not allow to perform the necessary redefinition . And here comes the help of binding through Mockito.do...
Another situation where you cannot do without the methods of Mockito.do...
is the redefinition of the method returning void
: waiting for the parameter Mockito.when
cannot work with this method. Mockito.doReturn
here, of course, not in the business, but there is Mockito.doThrow
, Mockito.doAnswer
and quite rarely come in handy Mockito.doNothing
.
Next, I will discuss in a little more detail how to set conditions and results of calls. I will consider only binding through Mockito.when
- an alternative method is almost completely analogous to handling.
The example above concerns a method without parameters, and the condition of a call associated with it is possible one thing - the fact of the call itself. As soon as parameters appear, the situation becomes more complicated. At a minimum, to call a method whose behavior I set, I need to pass something to it. But more importantly: it may turn out that I do not always want to receive the specified reaction, but only when calling with parameters that meet certain requirements. DataService
has this method:
String getDataItemById(String id) { // some code... }
If I need to set a reaction to any call to this method regardless of the arguments, I have to use the Mockito.any
method:
Mockito.when(dataService.getDataItemById(any())) .thenReturn("dataItem");
If I want the mock to respond only to a specific value of the argument, you can use this value directly or the methods of Mockito.eq
(if we are talking about equivalence) or Mockito.same
(if comparison of links is required):
Mockito.when(dataService.getDataItemById("idValue")) .thenReturn("dataItem"); // or Mockito.when(dataService.getDataItemById(Mockito.eq("idValue"))) .thenReturn("dataItem");
And if I want the argument to meet some requirements, there are a number of convenient specialized static methods of the same Mockito
class (for example, strings can be checked for content at the beginning or at the end of a certain sequence of characters, for pattern matching, etc.). There is also a common method Mockito.argThat (and its analogues for primitive types), which accepts the implementation of the ArgumentMatcher functional interface:
Mockito.when(dataService.getDataById( Mockito.argThat(arg -> arg == null || arg.length() > 5))) .thenReturn("dataItem");
The ArgumentMatchers
and AdditionalMatchers
classes allow you to work with some useful ready-made implementations of this interface. For example, AdditionalMatchers.or
and AdditionalMatchers.and
allow to combine other matchers (note: the static methods of these classes do not return instances of matchers, but only refer to them!)
For the same method, you can set the behavior several times with different requirements for the arguments, and all behaviors defined in this way will act simultaneously. Of course, in some cases they may intersect - for example, I will require returning one result when getting the value of an int
parameter less than 5 and the other when getting an even value. In this situation, the priority is given to the behavior that is specified later. Therefore, when defining complex behavior patterns, one should start with the weakest requirements (in the limit, any()
) and only then proceed to more specific ones.
When working with methods with more than one argument, the specified requirements are combined in accordance with the logical AND, that is, to obtain a given result, EACH of the arguments must meet the stated requirement. I did not find a way to specify an arbitrary way to combine them, although it may exist.
In addition, when specifying the behavior of this method, it is impossible to combine the static Mockito
methods used by Mockito
and the direct transfer of values. Use Mockito.eq
or Mockito.same
.
After the mock object method is called, the object must respond to the call. The main possible consequences are returning the result and throwing an exception, and it is these options that are primarily intended for the Mockito toolkit.
In the simplest case already shown above, the response to the call is the return of the value. I will give his code again:
List<String> data = new ArrayList<>(); data.add("dataItem"); Mockito.when(dataService.getAllData()).thenReturn(data);
Please note: you can return only an object; there are no separate methods for primitives. Therefore, if the method returns a primitive value, un / boxing will occur in such a situation. In most cases, this does not interfere at all, but if the compiler thinks otherwise, you will have to somehow negotiate with it ... or accept its warnings.
Throwing exceptions is no more difficult:
Mockito.when(dataService.getDataById("invalidId")) .thenThrow(new IllegalArgumentException());
There is another way: you can create an exception object and throw it directly, or you can provide a Mockito only an exception class so that it is automatically created:
Mockito.when(dataService.getDataById("invalidId")) .thenThrow(IllegalArgumentException.class);
In both cases, the syntax allows you to use and checked exceptions, however, Mockito will not allow you to run such a test if the exception type does not match the method I want to force to throw this exception.
When using a class as a parameter, constructors (even without parameters), as well as direct initialization of fields, are ignored - an object is created to bypass them (after all, it's Mockito!), So all fields of the thrown exception will be null
. Therefore, if the contents of the exception matter to you (say, some type
field that has a default value), you will have to abandon this method and create exceptions manually.
These response options are suitable if, in response to a call with the given conditions, you always have to return a certain, always the same result value or always throw the same exception, and in most cases these possibilities are quite enough. But what if you need more flexibility? Suppose my method takes a collection of values, and returns another collection of values ββassociated with the first one to one (for example, retrieving a collection of data objects based on their ID), and I want to use this mock object repeatedly with different sets of inputs in the test data, getting each time the corresponding result. You can, of course, describe separately the reaction to each specific set of parameters, but there is a more convenient solution - the Mockito.thenAnswer
method, also Mockito.then
as Mockito.then
. It accepts the implementation of the Functional Answer
interface, whose only method receives an object of class InvocationOnMock
. I can request the parameters of the method call (one by number or all at once as an array) from the latter and deal with them as I please. For example, you can get the corresponding value for each of the elements of my collection, form a new collection of them and return it (note: the desired result simply returns, and is not written to some field of the parameter object, as you would expect):
Mockito.when(dataService.getDataByIds(Mockito.any())) .thenAnswer(invocation -> invocation .<List<String>>getArgument(0).stream() .map(id -> { switch (id) { case "a": return "dataItemA"; case "b": return "dataItemB"; default: return null; } }) .collect(Collectors.toList()));
Ideologically, this is something like writing a model of a real method: getting parameters, processing, returning a result. - , - , , , mock- .
Answer
, , β , AnswersWithDelay
, ReturnsElementsOf
. .
: InvocationOnMock
β Object[]
, generic-.
β thenCallRealMethod
. . mock-, spy-. mock , , - null
. spy thenCallRealMethod
spy ; , - .
thenAnswer
: InvocationOnMock
callRealMethod()
β , "" - .
OngoingStubbing
OngoingStubbing
, , , . , . thenReturn
thenThrow
, varargs. .
Mockito.when(dataService.getDataById("a")) .thenReturn("valueA1", "valueA2") .thenThrow(IllegalArgumentException.class);
"valueA1
, β "valueA2
( ), ( ) IllegalArgumentException
.
: (mock' ), . , : , , . verify
.
, , :
Mockito.verify(dataService).getDataById(Mockito.any());
, getDataById
, , . , Mockito, when
, , , mock-. , , , when
, β mock', (. ).
:
Mockito.verify(dataService, Mockito.times(1)) .getDataById(Mockito.any());
Mockito.times
; Mockito.never
. Mockito.atLeast
( Mockito.atLeastOnce
1) Mockito.atMost
, , Mockito.only
, , mock- (. . ).
, Mockito
, VerificationAfterDelay
VerificationWithTimeout
, Mockito.after
Mockito.timeout
. For example:
Mockito.verify(dataService, Mockito.after(1000).times(1)) .getDataById(Mockito.any());
, mock , , , . . after
timeout
, , , β , . , timeout
β . VerificationWithTimeout
never
atMost
: .
, Mockito.any()
. , , β Mockito , , . Mock- , , , , :
dataService.getDataById("a"); dataService.getDataById("b"); Mockito.verify(dataService, Mockito.times(2)).getDataById(Mockito.any()); Mockito.verify(dataService, Mockito.times(1)).getDataById("a"); Mockito.verify(dataService, Mockito.never()).getDataById("c"); dataService.getDataById("c"); Mockito.verify(dataService, Mockito.times(1)).getDataById("c"); Mockito.verifyNoMoreInteractions(dataService);
verifyNoMoreInteractions
( verifyZeroInteractions
) β - ( verify
) mock- β . : varargs, , , !
, , , . , InOrder
:
InOrder inOrder = Mockito.inOrder(dataService);
varargs; β mock- , InOrder
. verify
, Mockito.verify
:
inOrder.verify(dataService, times(2)).saveData(any()); inOrder.verify(dataService).getData();
, saveData
, β getData
. , InOrder
, β .
, , β , . - , , β , , . ArgumentCaptor
capture()
. For example:
DataSearchRequest request = new DataSearchRequest("idValue", new Date(System.currentTimeMillis()), 50); dataService.getDataByRequest(request); ArgumentCaptor<DataSearchRequest> requestCaptor = ArgumentCaptor.forClass(DataSearchRequest.class); Mockito.verify(dataService, times(1)).getDataByRequest(requestCaptor.capture()); assertThat(requestCaptor.getAllValues()).hasSize(1); DataSearchRequest capturedArgument = requestCaptor.getValue(); assertThat(capturedArgument.getId()).isNotNull(); assertThat(capturedArgument.getId()).isEqualTo("idValue"); assertThat(capturedArgument.getUpdatedBefore()).isAfterYear(1970); assertThat(capturedArgument.getLength()).isBetween(0, 100);
ArgumentCaptor
, , ArgumentCaptor
. getValue()
, getAllValues()
β . , , .
, mock- , β @Mock
- :
MockitoAnnotations.initMocks(this);
( , mock', )
spy @Spy
β @Mock
β¦ spy , , ? , β spy .
@Captor
ArgumentCaptor
β , , .
@InjectMocks
. - Mockito, . mock- , . , . - , null
, - . ( ) dependency injection.
, : mock (spy, argument captor...), , , . , mock' β , . JUnit , , TestNG β . , , mock' , , , . . , , β , .
, mock- . TestNG @BeforeMethod
( @AfterMethod
). mock' , , ( JUnit β @Before
).
, , β Mockito.reset
Mockito.clearInvocations
. varargs, mock'. , . : (, ) , / mock' , β . , mock' . . , , .
(, ) β MockitoAnnotations.initMocks(this);
. "" , Mockito.
β Mockito. . mock- , ( mock' ). , MockitoSession
, . TestNG:
@Mock DataService dataService; MockitoSession session; @BeforeMethod public void beforeMethod() { session = Mockito.mockitoSession() .initMocks(this) .startMocking(); } @Test public void testMethod() { // some code using the dataService field } @AfterMethod public void afterMethod() { session.finishMocking(); }
, β , "" (, ) , .
Mockito: mock spy-, . , . , , :
MockSettings
( β , mock' - );MockingDetails
;BDDMockito
Mockito
;Mockito . javadoc' Mockito
.
, , .
Source: https://habr.com/ru/post/444982/
All Articles