Initialising mock objects - Mockito
For the mocks initialization, using the runner or the MockitoAnnotations.initMocks
are strictly equivalent solutions. From the javadoc of the MockitoJUnitRunner :
JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.
The first solution (with the MockitoAnnotations.initMocks
) could be used when you have already configured a specific runner (SpringJUnit4ClassRunner
for example) on your test case.
The second solution (with the MockitoJUnitRunner
) is the more classic and my favorite. The code is simpler. Using a runner provides the great advantage of automatic validation of framework usage (described by @David Wallace in this answer).
Both solutions allows to share the mocks (and spies) between the test methods. Coupled with the @InjectMocks
, they allow to write unit tests very quickly. The boilerplate mocking code is reduced, the tests are easier to read. For example:
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock(name = "database") private ArticleDatabase dbMock;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks private ArticleManager manager;
@Test public void shouldDoSomething() {
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
manager.finishArticle();
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros: The code is minimal
Cons: Black magic. IMO it is mainly due to the @InjectMocks annotation. With this annotation "you loose the pain of code" (see the great comments of @Brice)
The third solution is to create your mock on each test method.
It allow as explained by @mlk in its answer to have "self contained test".
public class ArticleManagerTest {
@Test public void shouldDoSomething() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleCalculator calculator = mock(ArticleCalculator.class);
ArticleDatabase database = mock(ArticleDatabase.class);
UserProvider userProvider = spy(new ConsumerUserProvider());
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros: You clearly demonstrate how your api works (BDD...)
Cons: there is more boilerplate code. (The mocks creation)
My recommandation is a compromise. Use the @Mock
annotation with the @RunWith(MockitoJUnitRunner.class)
, but do not use the @InjectMocks
:
@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Spy private UserProvider userProvider = new ConsumerUserProvider();
@Test public void shouldDoSomething() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.initiateArticle();
// then
verify(database).addListener(any(ArticleListener.class));
}
@Test public void shouldDoSomethingElse() {
// given
ArticleManager manager = new ArticleManager(calculator,
userProvider,
database);
// when
manager.finishArticle();
// then
verify(database).removeListener(any(ArticleListener.class));
}
}
Pros: You clearly demonstrate how your api works (How my ArticleManager
is instantiated). No boilerplate code.
Cons: The test is not self contained, less pain of code
Initialize Mocks in test before @BeforeStep
After some reflexion with a co-worker he gave me a solution :
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NedBatchApplication.class)
public class CustomReaderTest {
CustomReader customReader;
@Mock
RestApiService restApiService;
@Before
private void setup() {
MockitoAnnotations.initMocks(this);
Mockito.when(restApiService.getData().thenReturn(expectedData);
this.customReader = new CustomReader(restApiService);
}
@Test
public void test() {
customReader.initialize();
(...)
}
}
How to mock objects initialized in constructor using an external method call?
Create a package-private constructor that takes both objects as its parameters. Place your unit test in the same package (but in src/test/java/) so it has access to that constructor. Send in mocks to that constructor:
final HttpClient httpClient;
final HttpUtils httpUtils;
@Autowired
public SampleConstructor(HttpUtils httpUtils) {
this(ApacheHttpSingleton.getHttpClient(), httpUtils);
}
// For testing
SampleConstructor(HttpClient httpClient, HttpUtils httpUtils) {
this.httpClient = httpClient;
this.httpUtils = httpUtils;
}
Then in your test:
@Mock
HttpUtils mockHttpUtils;
@Mock
HttpClient mockHttpClient;
SampleConstructor c = new SampleConstructor(mockHttpClient, mockHttpUtils);
Mockito: When is @Mock object get initialized and which constructor it calls
Mock objects created with Mockito don't call any constructor or static initializer. (This is achieved through Objenesis in older versions of Mockito, and ByteBuddy in newer versions.) Consequently, all of the fields are uninitialized, and no side effects in constructors happen at all including any exceptions you might see thrown.
In contrast, spy objects do have their constructors called. Mockito will default to calling a no-argument constructor (public or private) if you don't initialize the field, and you can call the constructor of your choice inside the initializer.
The order of @Mock annotation initialization depends on which technique you use to initialize mocks:
- If you use
MockitoJUnitRunner
, mocks are initialized after initializer blocks, constructors, and @Rules, and before any other @Befores as defined in BlockJUnit4ClassRunner. - If you use
MockitoRule
, mocks are initialized before any @Before methods, but in undefined order compared to other @Rules unless you chain them manually withRuleChain
. - If you use
MockitoAnnotations.initMocks()
, mocks are initialized exactly when you call that method, which is after initializer blocks and rules, and (if you call within a @Before method) in undefined order compared to other @Before methods.
Is it possible to initialize some of the fields in a mock object
Not all objects that your object under test interacts with need to be mocks.
Remember that you can use POJOs as well.
DataResult
looks like a perfect candidate for a POJO.
You gain nothing by using a mock objet if you can create a POJO with desired state and behaviour.
Looking at the posted code, it looks like it is easy to create:
new DataResult<DataCar>("", "", new DataCar())
On top of that:
Your code looks suspicious to me.
- when stubbing
remoteService.loadData()
you create a new instance ofDataResult
- subsequently, you stub some calls on
dataResult
, which is not an object returned fromremoteService.loadData()
And to answer original post:
You can set fields on mocks (directly if access modifiers allow it, or via reflection otherwise). Note that this is highly not-idiomatic and surprising use of mocks.
class A {
B b;
}
class B {
boolean hasHeaders() {
return true;
}
}
@ExtendWith(MockitoExtension.class)
public class AAATest {
@Mock
A aMock;
@Mock
B bMock;
@BeforeEach
void setupMocks() {
aMock.b = bMock;
}
@Test
void testFieldInMockIsInitialized() {
Assertions.assertEquals(bMock, aMock.b);
}
}
Mockito: Mock private field initialization
I already found the solution to this problem which I forgot to post here.
@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {
@Mock
Person person;
@Test
public void testPrintName() throws Exception {
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Test test= new Test();
test.testMethod();
}
}
Key points to this solution are:
Running my test cases with PowerMockRunner:
@RunWith(PowerMockRunner.class)
Instruct Powermock to prepare
Test.class
for manipulation of private fields:@PrepareForTest({ Test.class })
And finally mock the constructor for Person class:
PowerMockito.mockStatic(Person.class);
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Related Topics
The Difference Between the Runnable and Callable Interfaces in Java
Is a Java String Really Immutable
Specifying Java Version in Maven - Differences Between Properties and Compiler Plugin
Subclassing a Java Builder Class
Can Constructors Throw Exceptions in Java
Quickest Way to Find Missing Number in an Array of Numbers
How to Define a List Bean in Spring
Does a Tcp Socket Connection Have a "Keep Alive"
Converting Many 'If Else' Statements to a Cleaner Approach
Finding Key Associated with Max Value in a Java Map
How to Create Executable Java Program
How to Have 2 Jvms Talk to One Another
How to Set Up Jax-Rs Application Using Annotations Only (No Web.Xml)