Initialising Mock Objects - Mockito

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 with RuleChain.
  • 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 of DataResult
  • subsequently, you stub some calls on dataResult, which is not an object returned from remoteService.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:

  1. Running my test cases with PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Instruct Powermock to prepare Test.class for manipulation of private fields: @PrepareForTest({ Test.class })

  3. And finally mock the constructor for Person class:

    PowerMockito.mockStatic(Person.class);
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);



Related Topics



Leave a reply



Submit