Injecting multiple implementations of same interface through Instance<T> in test class - java

I'm struggling with writing unit tests for one of my classes. The main class:
public class MainClass {
...
private Instance<BaseInterface> steps;
#Inject
public void setSteps(Instance<BaseInterface> steps) { this.steps = steps; }
#Asynchronous
public void callingMethod() {
...
ImmutableList<BaseInterface> stepList = STEP_ORDERING.immutableSortedCopy(steps); // Here the NPE is being thrown when calling method from test class
...
}
}
There are 5 specific implementations of BaseInterface, all 5 are correctly injected through setter by CDI at runtime (one of the implementation is annotated with #Named, the rest do not have this annotation).
However, calling the wrapper method callingMethod from the test class throws NPE. The test class:
#RunWith(MockitoJUnitRunner.class)
public class MainClassTest {
#InjectMocks
private MainClass mainClass = new MainClass();
#Mock
private Instance<BaseInterface> steps;
#Test
public void test() {
...
mainClass.callingMethod(); // Throws NPE
...
}
}
As far as I'm concerned, Mockito does not take care of bean dependency injection like CDI. Therefore, is there a way in which to tell Mockito to mock all implementations of BaseInterface and inject them into the MainClass?
Version details: Java 8, Mockito 2.8.9, JUnit 4
Thank you!

Solved. The problem was not caused by Mockito, but by the method STEP_ORDERING.immutableSortedCopy which behind the scenes calls iterator() method from Instance<T> which returned null (since it extends Iterable<T>).
Mocking the iterator does the trick:
Iterator<BaseInterface> iteratorMock = (Iterator<BaseInterface>) mock(Iterator.class);
when(steps.iterator()).thenReturn(iteratorMock);
or by creating an actual instance of iterator:
Iterator<BaseInterface> iteratorActual = new Iterator<BaseInterface>(){
// implementation here
};
when(steps.iterator()).thenReturn(iteratorActual );

Related

Strict #MockBean in a Spring Boot Test

I am developing a Spring Boot application. For my regular service class unit tests, I am able to extend my test class with MockitoExtension, and the mocks are strict, which is what I want.
interface MyDependency {
Integer execute(String param);
}
class MyService {
#Autowired MyDependency myDependency;
Integer execute(String param) {
return myDependency.execute(param);
}
}
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#Mock
MyDependency myDependency;
#InjectMocks
MyService myService;
#Test
void execute() {
given(myDependency.execute("arg0")).willReturn(4);
myService.execute("arg1"); //will throw exception
}
}
In this case, the an exception gets thrown with the following message (redacted):
org.mockito.exceptions.misusing.PotentialStubbingProblem:
Strict stubbing argument mismatch. Please check:
- this invocation of 'execute' method:
myDependency.execute(arg1);
- has following stubbing(s) with different arguments:
1. myDependency.execute(arg0);
In addition, if the stubbing was never used there would be the following (redacted):
org.mockito.exceptions.misusing.UnnecessaryStubbingException:
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at MyServiceTest.execute()
However, when I use #MockBean in an integration test, then none of the strict behavior is present. Instead, the stubbed method returns null because the stubbing "fails" silently. This is behavior that I do not want. It is much better to fail immediately when unexpected arguments are used.
#SpringBootTest
class MyServiceTest {
#MockBean
MyDependency myDependency;
#Autowired
MyService myService;
#Test
void execute() {
given(myDependency.execute("arg0")).willReturn(4);
myService.execute("arg1"); //will return null
}
}
Is there any workaround for this?
Yes there are some workarounds but it is quite involved.
It may be better to just wait for Mockito 4 where the default will be strict mocks.
The first option:
Replace #MockBean with #Autowired with a test configuration with #Primary ( this should give the same effect as #MockBean, inserting it into the application as well as into the test )
Create a default answer that throws an exception for any unstubbed function
Then override that answer with some stubbing - but you have to use doReturn instead of when thenReturn
// this is the component to mock
#Component
class ExtService {
int f1(String a) {
return 777;
}
}
// this is the test class
#SpringBootTest
#RunWith(SpringRunner.class)
public class ApplicationTests {
static class RuntimeExceptionAnswer implements Answer<Object> {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
throw new RuntimeException(
invocation.getMethod().getName() + " was not stubbed with the received arguments");
}
}
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public ExtService mockExtService() {
ExtService std = Mockito.mock(ExtService.class, new RuntimeExceptionAnswer());
return std;
}
}
// #MockBean ExtService extService;
#Autowired
ExtService extService; // replace mockBean
#Test
public void contextLoads() {
Mockito.doReturn(1).when(extService).f1("abc"); // stubbing has to be in this format
System.out.println(extService.f1("abc")); // returns 1
System.out.println(extService.f1("abcd")); // throws exception
}
}
Another possible but far from ideal option: instead of using a default answer is to stub all your function calls first with an any() matcher, then later with the values you actually expect.
This will work because the stubbing order matters, and the last match wins.
But again: you will have to use the doXXX() family of stubbing calls, and worse you will have to stub every possible function to come close to a strict mock.
// this is the service we want to test
#Component
class ExtService {
int f1(String a) {
return 777;
}
}
// this is the test class
#SpringBootTest
#RunWith(SpringRunner.class)
public class ApplicationTests {
#MockBean ExtService extService;
#Test
public void contextLoads() {
Mockito.doThrow(new RuntimeException("unstubbed call")).when(extService).f1(Mockito.any()); // stubbing has to be in this format
Mockito.doReturn(1).when(extService).f1("abc"); // stubbing has to be in this format
System.out.println(extService.f1("abc")); // returns 1
System.out.println(extService.f1("abcd")); // throws exception
}
}
Yet another option is to wait until after the test finishes using the mock, and then use
verifyNoMoreInteractins();
As mentioned in this comment, this GitHub issue in the spring-boot project addresses this same problem and has remained open since 2019, so it's unlikely that an option for "strict stubs" will be available in #SpringBootTest classes anytime soon.
One way that Mockito recommends to enable "strict stubs" is to start a MockitoSession with Strictness.STRICT_STUBS before each test, and close the MockitoSession after each test. Mockito mocks for #MockBean properties in #SpringBootTest classes are generated by Spring Boot's MockitoPostProcessor, so a workaround would need to create the MockitoSession before the MockitoPostProcessor runs. A custom TestExecutionListener can be implemented to handle this, but only its beforeTestClass method would run before the MockitoPostProcessor. The following is such an implementation:
public class MyMockitoTestExecutionListener implements TestExecutionListener, Ordered {
// Only one MockitoSession can be active per thread, so ensure that multiple instances of this listener on the
// same thread use the same instance
private static ThreadLocal<MockitoSession> mockitoSession = ThreadLocal.withInitial { null };
// Count the "depth" of processing test classes. A parent class is not done processing until all #Nested inner
// classes are done processing, so all #Nested inner classes must share the same MockitoSession as the parent class
private static ThreadLocal<Integer> depth = ThreadLocal.withInitial { 0 };
#Override
public void beforeTestClass(TestContext testContext) {
depth.set(depth.get() + 1);
if (depth.get() > 1)
return; // #Nested classes share the MockitoSession of the parent class
mockitoSession.set(
Mockito.mockitoSession()
.strictness(Strictness.STRICT_STUBS)
.startMocking()
);
}
#Override
public void afterTestClass(TestContext testContext) {
depth.set(depth.get() - 1);
if (depth.get() > 0)
return; // #Nested classes should let the parent class end the MockitoSession
MockitoSession session = mockitoSession.get();
if (session != null)
session.finishMocking();
mockitoSession.remove();
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
Then, MyMockitoTestExecutionListener can be added as a listener in test classes:
#SpringBootTest
#TestExecutionListeners(
listeners = {MyMockitoTestExecutionListener.class},
mergeMode = MergeMode.MERGE_WITH_DEFAULTS
)
public class MySpringBootTests {
#MockBean
Foo mockFoo;
// Tests using mockFoo...
}

Unit testing a method of a class that has a constructor and an autowired field that needs to be mocked using mockito

I have a service class that extends another service with a constructor. This class has an autowired field and a method that I wanted to unit test using Mockito. However, I am having trouble writing a unit for it.
Let say the service looks somewhat like this:
#Service
public class SomeService extends Service {
#Autowired
private SomeClient someClient;
public SomeService(Product product, #Qualifier(Constants.SOME_BEAN) Details temp) {
super(product, temp);
}
#Override
public State create() {
Request request = new Request();
request.set...
request.set..
Status status = someClient.createTenant(request);
..
.. // construct a State object and return
}
}
Now, I am writing a test for this class and I am trying to unit test the method create() above. I am having trouble mocking someClient there when this method is called by my test.
The test class looks somewhat like:
#RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
private Detail temp;
private SomeFacade service;
private SomeClient someClient;
private Product product;
#Before
public void setup() {
temp = mock(Details.class);
product = mock(Product.class);
service = spy(new SomeService(product, temp));
someClient = mock(SomeClient.class);
}
#Test
public void test() {
Status status = new Status();
status.setStatus(...);
when(someClient.createTenant(any())).thenReturn(status);
State state = service.create();
// asserts
}
}
What I want to is that when I call service.create in the test above, the call
someClient.createTenant(request); in the method tested should be mocked to return a value and this is something I am not able to get working. Any help?
We can use Mockito's #InjectMocks-annotation to inject mocks into our unit under test. For this, we also need to
remove mock intialization from the setup()-method and
annotate the mocks with #Mock
Remarks:
Instead of field injection, I would recommend constructor injection. This allows, among other things, to keep the structure of the code and create the unit under test within setup() by manual injection
I would also recommend to add a type to the when: when(someClient.createTennant(any(Request.class)))...
As was pointed out by #second, the spy on the unit under test is superfluous.
Also pointed out by #second was the fact that field injection will not work if a constructor is present

injecting Mocks through a chain of dependency classes using Mockito

Hi i have implemented a series of classes that use dagger 2 Injection to inject it dependencies for each class and i am trying to mock these to run my unit tests but it fails to initialise the dependencies found from the lower tier of classes that have dependencies.
This works fine in real enviornment but not for testing
I tried marking all the dependencies as either Spy or Mock but it only seems to inject the mocks on the first layer of my class that gets invoked. Below are three classes marked ExampleOne,Two,Three, followed by the test class.
ExampleOne has a method that calls another method from ExampleTwo, that then finally calls another from ExampleThree.
Only ExampleTwo gets the mock/spy injected fine but not ExampleThree.
public class ExampleOne{
#Inject
ExampleTwo exampleTwo;
//implementations below
public void doSomethingOne(){
exampleTwo.doSomethingTwo;
}
}
public class ExampleTwo{
#Inject
ExampleThree exampleThree
public void doSomethingTwo(){
exampleThree.doPrintHello();
}
}
public class ExampleThree{
public void doPrintHello(){
Log.d("Print","Hello")
}
}
Below is my test
#RunWith(MockitoJUnitRunner.class)
public class TestExamples(){
#InjectMocks
ExampleOne exampleOne
#Spy
ExampleTwo exampleTwo = new ExampleTwo();
#Spy
ExampleThree exampleThree = new ExampleThree();
#Test
void test(){
exampleOne.doSomethingOne();
//some testing code here
}
}
Practice explicit dependency principle either via constructor injection or method injection. Next, unit tests should be isolated. You should have no need to access implementation concerns in this case. Your classes are tightly coupled to implementation concerns and not abstractions which is a code smell.
public class ExampleOne {
ExampleTwo exampleTwo;
#Inject
public ExampleOne(ExampleTwo exampleTwo) {
this.exampleTwo = exampleTwo;
}
//implementations below
public void doSomethingOne(){
exampleTwo.doSomethingTwo();
}
}
public interface ExampleTwo {
void doSomethingTwo();
}
public class ConcreteExampleTwo implements ExampleTwo {
private ExampleThree exampleThree;
#Inject
public ConcreteExampleTwo(ExampleThree exampleThree) {
this.exampleThree = exampleThree;
}
public void doSomethingTwo(){
exampleThree.doPrintHello();
}
}
public interface ExampleThree {
void doPrintHello();
}
//...code removed for brevity
ExampleOne has one dependency at that level and if that dependencies are not able to be mocked/stubbed/faked without side effects then there is a problem with the design of the target class.
#RunWith(MockitoJUnitRunner.class)
public class TestExamples(){
#Mock
ExampleTwo exampleTwo;
#InjectMocks
ExampleOne exampleOne
#Test
void test(){
exampleOne.doSomethingOne();
verify(exampleTwo).doSomethingTwo();
}
}
With the above suggested changes ExampleOne can be tested in isolation without any knock on effects.
The concrete implementation of ExampleTwo can also be tested in isolation as well.

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.

Applying multiple permutations of mocks to dependencies for better unit testing in Spring

Let's say I have a class (OrchestratingClass) that has a method orchestrate() that calls a number of smaller methods of other classes (classA's do1() method, classB's do2() method). I would like to test the behavior of orchestrate() by mocking the responses of do1() and do2() with various permutations. I'm running my test with something like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public OrchestratingClassTest {
#Inject
private OrchestratingClass oc;
#Test
public void test1() {
// I would like to mock classA's do1() method to send back "1"
// I would like to mock classB's do2() method to send back "2"
}
#Test
public void test2() {
// I would like to mock classA's do1() method to send back "100"
// I would like to mock classB's do2() method to send back "200"
}
static class SimpleConfig {
#Bean
public InterfaceA interfaceA() {
return new ClassA();
}
#Bean
public InterfaceB interfaceB() {
return new ClassB();
}
#Bean
public OrchestratingClass orchestratingClass() {
return new OrchestratingClass();
}
}
}
And the orchestratingClass itself is quite basic, but I've added some sample code to aid in visualization:
#Named
public OrchestratingClass {
#Inject
private InterfaceA interfaceA;
#Inject
private InterfaceB interfaceB;
public String orchestrate() {
return interfaceA.do1() + " " + interfaceB.do2();
}
}
Now I'm aware I could tweak my SimpleConfig class to have the mocked out versions of classA and classB, but then I'm locked into 1 particular mock and can't "re-mock" things when I move onto test2(). I'm convinced that playing around with the java config files for 1 single test class would not work if we're trying to inject different "flavors" of beans "per-test". Does anyone have any recommendation on what I can do to make sure I'm really testing this thoroughly without being invasive (ex: adding superfluous "setters" for the specific classes in the orchestratingClass to side-step the bean injection pain)? Essentially, I'm looking to "tamper" the applicationContext on a per-test basis for specific beans of interest (along with the necessary housekeeping that's required) by applying a variety of mocks.
Here's a simple example using Mockito:
public class OrchestratingClassTest {
#Mock
private ClassA mockA;
#Mock
private ClassB mockB;
#InjectMocks
private OrchestratingClass oc;
#Before
public void prepare() {
MockitoAnnotations.initMocks(this);
}
#Test
public void shouldConcatenate() {
when(mockA.do1()).thenReturn("1");
when(mockB.do2()).thenReturn("2");
assertEquals("1 2", oc.orchestrate());
}
}
The magic happens in the call to MockitoAnnotations.initMocks(this), which will create mock instances for the fields annotated with #Mock, then create a real instance of Orchestrating class and inject its fields, thanks to #InjectMocks.
Note that, even without this magic, you could make your class easily testable by just adding a constructor taking ClassA and ClassB as arguments to OrchestratingClass, and annotate that constructor with #Autowired instead of annotating the fields. So, in short, use constructor injection rather than field injection.

Categories