How to Mock Optional Autowired field - java

I am trying to make a unit test with Mockito and I cannot find any way to mock my constructor's optional autowired field.
Here is my constructor:
#Autowired
public BatchInputManager(
BatchInputContentRepository batchInputContentRepository,
Optional<List<BatchInputExecutor>> batchInputExecutors) {
// ...
}
And here is how I try to mock these fields :
#InjectMocks
BatchInputManager batchInputManager;
#Mock
BatchInputContentRepository batchInputContentRepository;
#Mock
List<BatchInputExecutor> executors;
For the record, the BatchInputExecutor class is an abstract class and I defined a class that extends it in my test.
When I run my code, the optional that should contains all the extended classes of BatchInputExecutor is not empty, it is null; and the repository is not null.
How am I supposed to mock the value of the optional field in my constructor ?

I'd setup the mocks in the #BeforeEach/#BeforeAll method of the JUnit test.
#BeforeEach
public void mocking(){
var repoMock = ... mock repo ..
var executorMock = mock(BatchInputExecutor.class)
// configure executorMock here
var batchInputExecutors = Optional.of(List.of(executorMock))
var batchInputManager = new BatchInputManager()
... set the class level fields here ..
}

If you really want to use Mockito's annotations in this case:
Change this
#Mock
List<BatchInputExecutor> executors;
To this
#Mock
Optional<List<BatchInputExecutor>> executors;
And configure mockito to supports final classes and methods. See this https://stackoverflow.com/a/40018295/10744129

Related

How to inject #Value constructor parameter using Mockito annotation

I have a class:
public class MyClass{
MyClass(
#Value("${my.protocol}") String protocol,
#Value("${my.host}") String host,
#Value("${my.port}") int port,
#Autowired MyService myservice) {
........
}
....
}
I then wrote a test that uses Mockito:
#ExtendWith(MockitoExtension.class)
class MyClassTest {
#Mock
private MyService myservice;
#InjectMocks
private MyClass myClass;
....
}
The test failed with this:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'myClass'! Cause: the type 'MyClass ' has no default constructor
You haven't provided the instance at field declaration so I tried to construct the instance.
Examples of correct usage of #InjectMocks:
#InjectMocks Service service = new Service();
#InjectMocks Service service;
//and... don't forget about some #Mocks for injection :)
I think this is because I only provided one of the 4 construction parameters, not the other three which has #Value annotation.
Can someone please let me know how I can inject the three #Value construction parameters in order for this to work?
I use Junit 5 and Mockito.
Annotation-based magic is showy, but it's not the solution to every problem. Just do things the old-fashioned way:
#BeforeEach
void setup() {
this.subject = new MyClass("http", "localhost", 5000, mockService);
}
Mockito uses reflection inorder to initialize your instances so there will be no injection happening at the initialization step, it'll simply get the constructor and issue #invoke() method on it.
What you should do in this case is mock the values instead of mocking the whole container, the container here is MyClass.
I'm assuming you are using a .yml or a .properties file to assign the values, by creating a new application-${env}.yml this way you'll have your test values separately from the prod environment values.
Or if you have different values for each case, then before each replace the MyClass instance that's in the context with the one you modify:
#BeforeEach
void beforeEach() {
MyClass bean =// get the context and get a reference of `MyClass` bean
bean = new MyClass(...);
}

How to mock only one method of one injected dependency and inject the rest normally?

I'm writing tests for a project with a heavy use of dependency injection.
So normally I would just inject the object that I'm testing:
public class RegistrationTest
extends WithApplication {
private RegistrationController controller;
#Before
public void setUp() throws Exception {
Injector injector = app.injector();
controller = injector.instanceOf(RegistrationController.class);
}
#Test
public void openRegistrationView() {
Result result = controller.registrationForm();
assertEquals(OK, result.status());
}
}
Now I need to mock a return value of a method of one of the dependencies of the class under test.
To do this I can use #InjectMocks and #RunWith(MockitoJUnitRunner.class) to inject mocked dependencies.
#RunWith(MockitoJUnitRunner.class)
public class RegistrationTest
extends WithApplication {
#InjectMocks
private RegistrationController controller;
#Mock
private FormFactory formFactory;
#Mock
private RegistrationIpCache registrationIpCache;
#Test
public void openRegistrationView() {
doReturn("test").when(registrationIpCache).getLast();
Result result = controller.registrationForm();
assertEquals(OK, result.status());
}
}
But while I only want to mock one method call of RegistrationIpCache I need to mock the whole class and also I need to mock all other dependencies of RegistrationController like FormFactory in my example. Now every method of all mocked classes doesn't work as expected.
Sure I can use doCallRealMethod() to mock every method of all dependencies
doCallRealMethod().when(formFactory).form(Registration.class);
But as you already can imagine the work and the amount of boilerplate code needed for this to work is extreme.
Isn't it possible to inject my class normally and then only spy one dependency or mock one method of one dependency?
You need to use #Spy instead of a #Mock.
#InjectMocks
private RegistrationController controller;
#Mock
private FormFactory formFactory;
#Spy
private RegistrationIpCache registrationIpCache;
But be aware that in this case #Spy will try to use default constructor.
If the default constructor is not available use explicit constructor call:
#Spy
private RegistrationIpCache registrationIpCache = new RegistrationIpCache(dependencies);
Here is the cases when Mockito isn't able to instantiate your class from #Spy javadoc:
Mockito will try to find zero argument constructor (even private) and
create an instance for you. But Mockito cannot instantiate inner
classes, local classes, abstract classes and interfaces.
Here is the example of manually injecting dependencies:
#RunWith(MockitoJUnitRunner.class)
public class RegistrationTest {
private RegistrationController controller;
private RegistrationIpCache spyRegistrationIpCache; //this is the dependencies that you need to spy
#Before
public void setUp() throws Exception {
spyRegistrationIpCache = spy(realInstanceOfregistrationIpCache);
controller = new RegistrationController(registrationIpCache, realInstanceOfFormFactory);
}
}
In this case there is much more code that you have to write, but this give you much more control over the object instantiation and dependency injection.
Note that using constructor instantiation is not required. Since you do it manually you can instantiate it however you want.

Injected Mock behavior not used in constructor

Using Mockito annotations (MockitoJUnitRunner.class, #InjectMocks and #Mock):
#RunWith(MockitoJUnitRunner.class)
public class TagRepositoryTest {
#InjectMocks
private TagRepository repository;
#Mock
private SetupDetails setupDetails;
....
}
I have the test target class using the injected dependency in the constructor:
public class TagRepository {
private final Collection<Tag> tags;
#Autowired
public TagRepository(SetupDetails setupDetails) {
this.tags = Arrays.asList(
new Tag("name", setupDetails.getSourceId()),
...
);
...
}
And I am currently stubbing the method call in #Setup or inside #Test with when():
when(setupDetails.getSourceId()).thenReturn("1");
This is not working as expected. Mockito seems to only stub the method call after the #InjectMocks TagRepository constructor is called, resulting in a null beeing returned instead of "1".
Is there a way to get the stub ready before the constructor is called (using Mockito annotations)?
The only way I am being able to work around this is trying to control the order Mockito setups this scenario giving up on Mockito annotations:
public void setUp() {
setupDetails = mock(SetupDetails.class);
when(setupDetails.getDbId()).thenReturn("1");
repository = new TagRepository(setupDetails);
}
Indeed this is the case and your "work around" is the way to go.
Some will argue that this is a good practice as your test will not compile when you introduce more members to your class under test, as you'll also add them to the constructor.

EasyMock: call order on mocks created with #Mock

Is there any way to verify methods call order between mocks if they are created with #Mock annotation?
As described in documentation it can be done with a mock control. But EasyMockRule does not expose control object.
I have looked at EasyMockSupport implementation, but have not found way to force it to use one control for all injected mocks. :(
public class Test extends EasyMockSupport {
#Rule
public EasyMockRule mocks = new EasyMockRule(this);
#Mock
private SomeClass first;
#Mock
private OtherClass second;
#TestSubject
private UnderTest subject = new UnderTest ();
#Test
public void test() {
expect(first.call());
expect(second.call());
....
//Verify that calls were in order first.call(), second.call()
}
}
You are right, it is not possible. An enhancement could be to allow to set a control in the #Mock annotation. Can you please file an issue?
In your case, you will have to create the mocks manually using the same IMocksControl like explained in the documentation.

Difference between #Mock and #InjectMocks

What is the difference between #Mock and #InjectMocks in Mockito framework?
#Mock creates a mock. #InjectMocks creates an instance of the class and injects the mocks that are created with the #Mock (or #Spy) annotations into this instance.
Note you must use #RunWith(MockitoJUnitRunner.class) or Mockito.initMocks(this) to initialize these mocks and inject them (JUnit 4).
With JUnit 5, you must use #ExtendWith(MockitoExtension.class).
#RunWith(MockitoJUnitRunner.class) // JUnit 4
// #ExtendWith(MockitoExtension.class) for JUnit 5
public class SomeManagerTest {
#InjectMocks
private SomeManager someManager;
#Mock
private SomeDependency someDependency; // this will be injected into someManager
// tests...
}
This is a sample code on how #Mock and #InjectMocks works.
Say we have Game and Player class.
class Game {
private Player player;
public Game(Player player) {
this.player = player;
}
public String attack() {
return "Player attack with: " + player.getWeapon();
}
}
class Player {
private String weapon;
public Player(String weapon) {
this.weapon = weapon;
}
String getWeapon() {
return weapon;
}
}
As you see, Game class need Player to perform an attack.
#RunWith(MockitoJUnitRunner.class)
class GameTest {
#Mock
Player player;
#InjectMocks
Game game;
#Test
public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
assertEquals("Player attack with: Sword", game.attack());
}
}
Mockito will mock a Player class and it's behaviour using when and thenReturn method. Lastly, using #InjectMocks Mockito will put that Player into Game.
Notice that you don't even have to create a new Game object. Mockito will inject it for you.
// you don't have to do this
Game game = new Game(player);
We will also get same behaviour using #Spy annotation. Even if the attribute name is different.
#RunWith(MockitoJUnitRunner.class)
public class GameTest {
#Mock Player player;
#Spy List<String> enemies = new ArrayList<>();
#InjectMocks Game game;
#Test public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
enemies.add("Dragon");
enemies.add("Orc");
assertEquals(2, game.numberOfEnemies());
assertEquals("Player attack with: Sword", game.attack());
}
}
class Game {
private Player player;
private List<String> opponents;
public Game(Player player, List<String> opponents) {
this.player = player;
this.opponents = opponents;
}
public int numberOfEnemies() {
return opponents.size();
}
// ...
That's because Mockito will check the Type Signature of Game class, which is Player and List<String>.
In your test class, the tested class should be annotated with #InjectMocks. This tells Mockito which class to inject mocks into:
#InjectMocks
private SomeManager someManager;
From then on, we can specify which specific methods or objects inside the class, in this case, SomeManager, will be substituted with mocks:
#Mock
private SomeDependency someDependency;
In this example, SomeDependency inside the SomeManager class will be mocked.
#Mock annotation mocks the concerned object.
#InjectMocks annotation allows to inject into the underlying object the different (and relevant) mocks created by #Mock.
Both are complementary.
#Mock creates a mock implementation for the classes you need.
#InjectMock creates an instance of the class and injects the mocks that are marked with the annotations #Mock into it.
For example
#Mock
StudentDao studentDao;
#InjectMocks
StudentService service;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Here we need the DAO class for the service class. So, we mock it and inject it in the service class instance.
Similarly, in Spring framework all the #Autowired beans can be mocked by #Mock in jUnits and injected into your bean through #InjectMocks.
MockitoAnnotations.initMocks(this) method initialises these mocks and injects them for every test method so it needs to be called in the setUp() method.
This link has a good tutorial for Mockito framework
A "mocking framework", which Mockito is based on, is a framework that gives you the ability to create Mock objects ( in old terms these objects could be called shunts, as they work as shunts for dependend functionality )
In other words, a mock object is used to imitate the real object your code is dependend on, you create a proxy object with the mocking framework.
By using mock objects in your tests you are essentially going from normal unit testing to integrational testing
Mockito is an open source testing framework for Java released under the MIT License, it is a "mocking framework", that lets you write beautiful tests with clean and simple API. There are many different mocking frameworks in the Java space, however there are essentially two main types of mock object frameworks, ones that are implemented via proxy and ones that are implemented via class remapping.
Dependency injection frameworks like Spring allow you to inject your proxy objects without modifying any code, the mock object expects a certain method to be called and it will return an expected result.
The #InjectMocks annotation tries to instantiate the testing object instance and injects fields annotated with #Mock or #Spy into private fields of the testing object.
MockitoAnnotations.initMocks(this) call, resets testing object and re-initializes mocks, so remember to have this at your #Before / #BeforeMethod annotation.
Though the above answers have covered, I have just tried to add minute detail s which i see missing. The reason behind them(The Why).
Illustration:
Sample.java
---------------
public class Sample{
DependencyOne dependencyOne;
DependencyTwo dependencyTwo;
public SampleResponse methodOfSample(){
dependencyOne.methodOne();
dependencyTwo.methodTwo();
...
return sampleResponse;
}
}
SampleTest.java
-----------------------
#RunWith(PowerMockRunner.class)
#PrepareForTest({ClassA.class})
public class SampleTest{
#InjectMocks
Sample sample;
#Mock
DependencyOne dependencyOne;
#Mock
DependencyTwo dependencyTwo;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
public void sampleMethod1_Test(){
//Arrange the dependencies
DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();
DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();
//call the method to be tested
SampleResponse sampleResponse = sample.methodOfSample()
//Assert
<assert the SampleResponse here>
}
}
Reference
One advantage you get with the approach mentioned by #Tom is that you don't have to create any constructors in the SomeManager, and hence limiting the clients to instantiate it.
#RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
#InjectMocks
private SomeManager someManager;
#Mock
private SomeDependency someDependency; // this will be injected into someManager
//You don't need to instantiate the SomeManager with default contructor at all
//SomeManager someManager = new SomeManager();
//Or SomeManager someManager = new SomeManager(someDependency);
//tests...
}
Whether its a good practice or not depends on your application design.
#Mock is used to declare/mock the references of the dependent beans, while #InjectMocks is used to mock the bean for which test is being created.
For example:
public class A{
public class B b;
public void doSomething(){
}
}
test for class A:
public class TestClassA{
#Mocks
public class B b;
#InjectMocks
public class A a;
#Test
public testDoSomething(){
}
}
#InjectMocks annotation can be used to inject mock fields into a test object automatically.
In below example #InjectMocks has used to inject the mock dataMap into the dataLibrary .
#Mock
Map<String, String> dataMap ;
#InjectMocks
DataLibrary dataLibrary = new DataLibrary();
#Test
public void whenUseInjectMocksAnnotation_() {
Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");
assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
}
Many people have given a great explanation here about #Mock vs #InjectMocks. I like it, but I think our tests and application should be written in such a way that we shouldn't need to use #InjectMocks.
Reference for further reading with examples: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
#Mock is for creating and injecting mock instances without having to call Mockito.mock manually. In this example the instance would be ClassB.
Whereas, #InjectMocks is for injecting mock fields into the tested object automatically. In this case it would be ClassA
Notice that that #InjectMocks are about to be deprecated
deprecate #InjectMocks and schedule for removal in Mockito 3/4
and you can follow #avp answer and link on:
Why You Should Not Use InjectMocks Annotation to Autowire Fields

Categories