Mockito Annotations
- @Mock: Creates a dummy implementation for an interface or a class in which you define the output of certain method calls. Mock objects are configured to perform a certain behavior during a test. This is not a real object and does not maintain the state changes to it.
- @Spy: Creates a real instance of the class and track every interactions with it. It maintains the state changes to it.
- @Captor: Creates an
ArgumentCaptor
instance which is used to capture method argument values for further assertions. - @InjectMocks: Create the object of class to be tested and than insert it’s dependencies (mocked) to completely test the behavior.
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock
HashMap<String, Integer> hashMap;
@Captor
ArgumentCaptor<String> keyCaptor;
@Captor
ArgumentCaptor<Integer> valueCaptor;
@Spy
@InjectMocks
Example example;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testMethod() {
// Given
// When
hashMap.put("A", 10);
// Then
Mockito.verify(hashMap).put(keyCaptor.capture(), valueCaptor.capture());
assertEquals("A", keyCaptor.getValue());
assertEquals(new Integer(10), valueCaptor.getValue());
}
}
Source
https://martinfowler.com/bliki/TestDouble.html
Mock manually
public class ExampleTest {
@Before
public void setUp() {
// Real instance
Sample sample = new Sample();
// Mocked instance
Sample sampleMocked = mock(Sample.class);
}
}
Understanding mocks
Mock objects are configured to perform a certain behavior during a test.
This is not a real object and does not maintain the state changes to it.
public class ExampleTest {
@Before
public void setUp() {
// Real instance
Sample sample = new Sample();
// Mocked instance
Sample sampleMocked = mock(Sample.class);
/**
* Fields
*/
// This will set value field to 2
sample.value = 2; // sample.value = 2
// This actually will NOT set value field to 2, it will be still its default value
sampleMocked.value = 2; // sampleMocked.value = 0
/**
* Methods
*/
// This call will run the real method and return a value according to the inputs
int result = sample.sum(1, 1); // result = 2
// This call will NOT run the real method and will return a default value according to the result type
int resultMocked = sampleMocked.sum(1, 1); // resultMocked = 0
}
}
Mocks allow us to focus in the tests of the a class, without taking into account the logic in other methods. For example:
Class to test:
public class Example {
@Autowired
private ARepository aRepository;
public String getById(int id) {
// Retrieves the object with the given ID from the database.
String obj = this.aRepository.findById(id).get();
// Some random logic
if(obj.equals("Not found")) {
return "Invalid ID";
}
return obj;
}
}
Test:
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock
private ARepository aRepository;
@Spy
@InjectMocks
Example example;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test_getById_when_validID_then_returnsObject() {
// Given
int id = 5;
// When
String result = getById(id); // result = null
/* getById will call aRepository.findById
but since it's a mock,
it won't really get the object from the database,
it won't do anything actually and it will just return a default value
(since it's a String, it will return null).
This allows us to test methods without making real calls to the database.
*/
// Then
// This will verify that we did call the desired methods.
Mockito.verify(aRepository).findById(eq(id));
}
}
Mocks also allow us to mock some classes and force desired outputs. For example:
@RunWith(MockitoJUnitRunner.class)
public class ExampleTest {
@Mock
private ARepository aRepository;
@Spy
@InjectMocks
Example example;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
public void test_getById_when_validID_then_returnsObject() {
// Given
int id = 5;
String obj = "Not found";
Mockito.when(aRepository.findById(eq(id))).thenReturn(obj);
// When
String result = getById(id);
// Then
Mockito.verify(aRepository).findById(eq(id));
assertEquals("Invalid ID", result);
}
}
Mock static methods
@RunWith(PowerMockRunner.class)
@PrepareForTest({Example.class, ExampleStatic.class}) // Mock the class under test and the classes with static methods we want to mock.
public class ExampleTest {
...
@Before
public void setUp() {
PowerMockito.mockStatic(ExampleStatic.class);
// Mock method
when(ExampleStatic.method(...)).return(...);
// Mock constructor
PowerMockito.whenNew(ExampleStatic.class).withArguments(...).thenReturn(...);
...
}
}
Other annotations
/*
Defer the loading of the classes listed in the annotation.
*/
@PowerMockIgnore({"javax.managment.*"})
/*
Suppress static initializers.
*/
@SuppressStaticInitialization({"javax.managment.RuntimeMXBean"})
public class ExampleTest {
}
@RunWith(PowerMockRunner.class) vs @RunWith(MockitoJUnitRunner.class)
Try to use always Mockito framework instead of PowerMockito one.
PowerMockito should be used as a last resort.
Link: