Difference between #InjectMocks and #Autowired usage in mockito? - java

When I was writing test case using the Mockito and Junit, I was using the #InjectMocks for the class to be tested. In other parts of project, I also see #Autowired is being used for the class to be tested.
When can I use #InjectMocks and #Autowired? What is the difference between two when we are trying to use them with class to be tested?

#InjectMocks is a Mockito mechanism for injecting declared fields in the test class into matching fields in the class under test. It doesn't require the class under test to be a Spring component.
#Autowired is Spring's annotation for autowiring a bean into a production, non-test class.
If you wanted to leverage the #Autowired annotations in the class under test, another approach would be to use springockito which allows you to declare mock beans so that they will be autowired into the class under test the same way that Spring would autowire the bean. But typically that's not necessary.

#InjectMocks annotation tells to Mockito to inject all mocks (objects annotated by #Mock annotation) into fields of testing object. Mockito uses Reflection for this.
#Autowired annotation tells to Spring framework to inject bean from its IoC container. Spring also uses reflection for this when it is private field injection.
You can even use even use #Inject annotation (part of Java EE specification) with the same effect.
But I would suggest to look at benefits of Constructor injection over Field injection. In that case you don't need to use #InjectMocks at all, because you can pass mocks into testing object via constructor. There wouldn't be Reflection needed under the hood in your test nor in production.
If you want to create integration test with subset of Spring beans I would suggest to take a look at #DirtiesContext annotation. It is part of Spring framework module commonly called "Spring Test".

Related

How to inject mock for only one test case with Quarkus/RestAssured

I'm attempting to test a REST controller (using Quarkus) endpoint using rest assured. I want to mock a class that is injected into that controller (ideally with Mockio), but only for one of my tests. Or get different behaviour per test case without having to have separate classes for each test. I'm not sure how to do this?
I've seen doing it the way from the documentation:
#Mock
#ApplicationScoped
public class MockExternalService extends ExternalService {
#Override
public String service() {
return "mock";
}
}
But this would only allow me to use one mock for all tests and not mock certain behaviours based on tests as I would with Mockito. I think?
I've tried creating a mock and annotating it with #Mock
#Mock
public TableExtractorService tableExtractorServiceMock = Mockito.mock(TableExtractorService.class);;
but I still get my real implementation when I use it. I'm using a constructor annotated with #Inject in my Controller that takes the TableExtractorService.
For a bit more information my test using restassured looks like this:
InputPart filePart = Mockito.mock(InputPart.class);
Mockito.when(tableExtractorServiceMock.Extract(anyObject()))
.thenThrow(IOException.class);
final InputStream inputStream = filePart.getBody(InputStream.class, null);
given()
.multiPart("file", inputStream)
.when().post("/document")
.then()
.statusCode(500);
That endpoint calls the service class that I'm trying to mock, and I want that mock to return an exception.
It can't be done. Quarkus documentation explains the issue:-
Although this mechanism is fairly straightforward to use, it nonetheless suffers from a few problems:
A new class (or a new CDI producer method) needs to be used for each bean type that requires a mock. In a large application where a lot of mocks are needed, the amount of boilerplate code increases unacceptably.
There is no way for a mock to be used for certain tests only. This is due to the fact that beans that are annotated with #Mock are normal CDI beans (and are therefore used throughout the application). Depending on what needs to be tested, this can be very problematic.
There is a no out of the box integration with Mockito, which is the de-facto standard for mocking in Java applications. Users can certainly use Mockito (most commonly by using a CDI producer method), but there is boilerplate code involved.
Link for reference: https://quarkus.io/blog/mocking/
According Quarkus test documentation, you can do it usingo #QuarkusMock or #InjectMock.
As #Ankush said, a class annotated with the #Mock annotation is using the CDI #Alternative mechanism, and will be global. #QuarkusTestProfiles can be used to define CDI #Alternatives for groups of tests.
For example, instead of annotating the mock with #Mock, it could be referenced in a test profile as
default Set<Class<?>> getEnabledAlternatives() {
return Set.of(MyMockThing.class);
}
Any test annotatated with the
#TestProfile(MyMockyTestProfile.class)
profile would get those mocks, while others would use the original implementation.
What may be a simpler method is to just use #InjectMock. For example, in the test class, declaring a field like this:
#InjectMock
MyThing mock;
will ensure that mock is used by the classes under test, just for this test.
For rest clients, it will also be necessary to add a #RestClient annotation, and if the original implementation is a singleton, convertscopes can be used to coax the scopes into something mockable.
#RestClient
#InjectMock(convertScopes = true)
MyThing mock;
Behaviour can be added to the injected mock in #BeforeEach or #BeforeAll methods. For example
#BeforeEach
public void setup() {
when(mock.someMethod()).thenReturn("some value");
}

Inject mocks into spring context WITHOUT spring trying to autowire the mock's fields

I have a class which I want to mock for a unit test. The class has a field with an #Autowired annotation. I need to inject that mock into a spring context that is being used in a junit. However, even though it's a mock, spring still sees the #Autowired and goes looking for an instance of this other class to inject. However, the context doesn't have an instance of that class, because one isn't needed (because the methods that might use that field are being mocked).
I've tried both XML and Java config, and in both cases the mock gets created and then spring still tries to find things to autowire into the mock (note I've set autowire=no in both cases).
XML that I've tried:
<bean class="org.easymock.EasyMock" factory-method="mock" autowire="no">
<constructor-arg value="com.stackoverflow.BeanClass" />
</bean>
Java config that I've tried:
#Bean(autowire = Autowire.NO)
public BeanClass beanClass1()
{
return EasyMock.createMock(BeanClass.class);
}
Note I've tried with both EasyMock and Mockito with the same results.
The only way I've found to do this is to do something like context.getBeanFactory().registerSingleton("name", mock) - but I can't do this if using SpringJUnit4ClassRunner.
Before anyone says "just don't do that", this is for an integration test rather than a pure unit test, and the complex wiring logic of some of the beans means that instantiating a spring context here saves a lot of duplicated effort in the test harness.
So, how can I stop spring trying to autowire beans into the fields of my mock?

Scalamock testing Java dependency injection

I use Scalamock for unit testing my Java project.
Project contains classes that use Java CDI style DI:
class Resource {
#Inject
private Service service;
.....
}
How can I inject mock as a Service instance?
With Mockito I'm able to use #InjectMocks, but I definitely want to use Scalamock.
Currently ScalaMock does not support Mockito's #InjectMocks equivalent.
In case of setter/constructor injection you can inject your mocks manually. In case of field injection you could access the private fields using reflection, but that would be very cumbersome.
Please create new ScalaMock feature request (https://github.com/paulbutcher/ScalaMock/issues) - if it gets upvoted we can implement it.

spring junit testing

I have a maven spring project (latest version) and I want to write some junit tests (latest version).
The issue I have is that my spring beans are autowired, and when I call them from junit test, I get null pointer exceptions, as spring doesn't autowire them.
How can I load the context so that things are autowired?
Have you studied Testing chapter in Spring reference documentation? Here is an example you should start with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class MyTest {
#Resource
private FooService fooService;
// class body...
}
If you are in com.example.MyTest in /src/test/java, you will need /src/test/resources/com/example/MyTest-context.xml - but the exceptions will show you the way.
This is a possibility:
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "/applicationContext.xml"
// in the root of the classpath
#ContextConfiguration({"/applicationContext.xml"})
public class MyTest {
// class body...
}
Usually it's a good idea though to have a test-applicationContext for your test infrastructure.
You should use the SpringJUnit4ClassRunner on your test classes, and use #Resource (or #Autowired) on the field in your test class that contains the bean. You should consider having a special test context Spring configuration that uses stubs so that your tests are genuine unit tests, and don't rely on the whole application context.

Mock object and Spring annotations

I am using Spring annotations in my code to do the DI. So lets say I have a class class1 that depends on another class class2, I define class1 as below:
#Component
public class class1 {
#Resource
private interface2 object2;
}
class2 is an implementation of interface2.
Now lets say I want to mock class2 and pass it to class1, I dont see any constructor or setter in class1. I think Spring uses reflection to inject object2. How can I mock it? Should I add a setter in class1? Or can I reuse the same way spring is doing it - I mean does spring itself have a mock object framework or something, I was planning to use EasyMock for the mocking.
Thanks
The ReflectionTestUtils class in Spring might be helpful.
It seems to do what you are looking for...at least the injection part :-)
Mockito has a really powerful way of handling mocks and DI:
#RunWith(MockitoJUnitRunner.class)
public class Class1Test {
#Mock
private Interface2 inteface2mock;
#InjectMocks
private Class1 class1;
#Test
public void someTest() {
when(interface2mock.doSomething("arg")).thenReturn("result");
String actual = class1.doSomeThatDelegatesToInterface2();
assertEquals("result", actual);
}
}
Read more about #InjectMocks in the Mockito javadoc or in a blog post that I wrote about the topic some time ago.
Available as of Mockito 1.8.3, enhanced in 1.9.0.
ReflectionTestUtils is the easiest to add the mock you want (we use JMock, but it does not really matter), drawback is that it is slightly brittle. If you rename the field, you must remember to change the test as well.
You can also use this: http://i-proving.com/2006/11/09/using-jmock-and-inject-object/
It describes how to use a mocked object in a spring context.
AFAIK, there is no mocking framework built into Spring, so you need to use something like EasyMock. The way I have done this in the past is to
Define the Spring config using XML instead of annotations*
The config for the main app is defined in appContext-main.xml and the test config (the mock objects) is defined in appContext-test.xml
A mock bean in appContext-test.xml must have the same ID as the corresponding bean in appContext-main.xml
When the app runs only appContext-main.xml is loaded
When the tests run both appContext-main.xml and appContext-test.xml are loaded. Make sure that they are loaded in this order so that the mocks 'override' any beans of the same name
* You don't need to need to convert all your Spring configuration to XML to use this approach. Only those beans that have mock implementations or have mock implementations injected into them need to be changed. The others bean can continue to be defined with annotations.
Injecting yourself via reflection is very easy, so you can avoid the setter method.
To do it yourself is like this:
for (Field field : injectable.getClass().getDeclaredFields()) {
MyAnnotation annotation = field.getAnnotation(MyAnnotation.class);
if (annotation != null) {
field.setAccessible(true);
Object param = generateMockObject();
field.set(injectable, param);
}
}
There is a JUnit Rule that gives EasyMock similar annotation-driven injection capabilities, in the manner of Mockito. See EasyMockRule

Categories

Resources