Since the page that introduces the description method of Mockito when testing with JUnit + Mockito is apt, I organized it in my own way.
Mock
It is used when you want to mock and replace all the methods of the target instance. In the default state, for objects, methods with a return value will return null. (In Collection, it's an empty Collection, in primitive values it's 0 or false ...)
It can be mocked by adding the @ Mock
annotation.
Also, by assigning @InjectMocks
to the instance of the class to be tested,
You can plug a mock instance of @ Mock
into an instance of a @ Inject
member in an instance.
Spy
Use this when you want to replace only a specific method of the target instance. I'm not very good at it. Is there a case where you want to replace only one method while using the getter and other methods as they are?
It can be used by annotating @Spy
.
You need to create the instance yourself.
It cannot be injected with @InjectMocks
.
Captor
Primarily used to capture and validate argument values. If the argument is an object, it cannot be verified with a standard matcher such as ʻeq`. At this time, Captor is effective.
When capturing arguments, define an instance of the ArgumentCaptor class and give @Captor
.
Assuming that you are designing using DI, we will summarize the basic description using Mockito.
--Set the mode of Mockito to Strict Stubs
.
--Mockito has three modes: Silent
, Strict
(v2 default), and StrictStubs
.
--If you set it to StrictStubs
, it will also detect the argument mismatch of the mock that becomes the stub, so set it to the strictest.
--In principle, initialization is done with annotations.
--There is also an initialization method using mock (XX.class)
, spy (new XX ())
, ʻArgumentCaptor.forClass (XX.class), but it is simpler to write using annotations. --Be careful not to target the interface to which
@InjectMocksis added. (cannot new) --You don't have to write
verify. --From v2, if the prepared mox tab is not called, an unchecked exception will occur, so I don't think it is necessary to verify with
verifyeach time it is called. --If so, what if you want to count the number of times you have been called multiple times? --Write the mock description in the format
doXX (). When ()`.
--There are pros and cons here, and it seems that official has not revealed its attitude. Is.
you may prefer to use these methods in place of the alternative with when(),
for all of your stubbing calls.
And that.
――Personally, it is troublesome to learn the description method one by one depending on the presence or absence of the return value and the difference between Mock and Spy. I think it will be complicated to look at.
--Therefore, unify to the format of doXX (). When ()
that can be used in all descriptions.
--However, in the case where the return value is changed by the call described later, the description in when (). ThenReturn ()
format is required.
@RunWith (MockitoJUnitRunner.StrictStubs.class)
test class to use Mockito in the test.
--You may make the same setting with @Rule
. (Strictness.STRICT_STUBS
)@InjectMocks
to the variable of the class to be tested.@ Mock
to the variable of the class you want to mock.
-Just add @InjectMocks and @Mock annotations to create an instance.import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
...
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class XXTest{
@InjectMocks
XXInteractor target;
@Mock
IXXRepository repoMock;
...
}
@Test
.When the test target is as follows
public class XXInteractor{
@Inject
IXXRepository repository;
XXEntity fetch(String id) throws NotFoundException{
try{
return repository.findBy(id);
}
catch(UseCaseNotFoundException e){
throw new NotFoundException();
}
}
}
You can write the following test.
private static final String ID = "ID";
@Test
public void testFetchSuccess() throws Exception{
//Returns a normal XXEntity only when an ID is given as an argument.(Otherwise, null)
doReturn(new XXEntity(ID))
.when(repoMock).findBy(eq(ID));
XXEntity result = target.fetch(ID);
assertThat(result.getId(), is(ID));
}
@Test(expected = NotFoundException.class)
public void testFetchFailureWithNotFound() throws Exception{
doThrow(new RepositoryNotFoundException())
.when(repoMock).findBy(eq(ID));
target.fetch(ID);
}
The basic mocking pattern is shown below.
//If there is a return value
doReturn(new XXEntity(ID))
.when(targetMock).fetch(eq(ID));
//If there is no return value
doNothing().when(targetMock).delete(eq(ID))
doThrow(new XXException()).when(targetMock).fetch(eq(ID))
doThrow(new XXException()).when(targetMock).delete(eq(ID))
//Returns a return value
doReturn(new XXEntity()).when(targetSpy).fetch()
//Raise an exception
doThrow(new XXException()).when(targetSpy).fetch()
doReturn(new XXEntity(ID))
.when(targetMock).fetch(any())
//Cases with no arguments, or cases where you want to return a different value for each call with the same arguments
//This case cannot be written with doReturn, so describe it in thenReturn format.
when(targetMock.getNextKey())
.thenReturn("ID-1") //First call
.thenReturn("ID-2"); //Second call
//Case that returns a different value for each argument(Define each normally)
doReturn(new XXEntity("ID-1")).when(targetMock).fetch(eq("ID-1");
doReturn(new XXEntity("ID-2")).when(targetMock).fetch(eq("ID-2");
If the argument is primitive, it can be verified only with ʻeq`, but if the argument is an object or list map system, it cannot be verified, so it must be combined with Captor.
When the test target is as follows
public class XXInteractor{
@Inject
IXXRepository repository;
void update(XXEntity entity){
repository.update(entity);
}
}
Verify as follows.
private static final String ID = "ID";
@InjectMocks
XXUseCase target;
@Mock
IXXRepository repoMock;
//Define Captor
@Captor
ArgumentCaptor<XXEntity> argCaptor;
@Test
public void testUpdateSuccess(){
//Define a mock to capture the arguments
doNothing().when(repoMock).update(argCaptor.capture());
target.update(ID);
//Validate the captured arguments
assertThat(argCaptor.getValue().getId(), is(ID));
}
A case of mocking a method that implements something like ref
in C # that uses part of the argument as the return value.
Since this method definition is not Testable, it should be basically solved by design change,
If you really want to rewrite the contents of the instance received as an argument, you can do it by the following method.
doAnswer( invocation -> {
Object[] args = invocation.getArguments();
//You can manipulate that instance by casting the arguments to that class.
//In this case, the second argument is rewritten, so args[1]
((XXOutputParam) args[1]).setId("ID");
return null; //Null if the return value is void
}).when(useCaseMock).get(any(), any())
Call it every time around the @Before
method.
reset(mock);
https://static.javadoc.io/org.mockito/mockito-core/2.23.4/org/mockito/Mockito.html
Recommended Posts