Actually I have the following problem:
I have a Test which need the real call from the service.
class ServiceATest{
#Spy serviceNeedRealValue service;
//other beans mocked there...
#InjectMocks private serviceA serviceA;
//The test method...
}
Well my problem comes, when I call the service because the service have an autowired bean which give me a null pointer, and I don't know how inject or tell to him is a mock.
class serviceNeedRealValue {
#Autowired aThingWhichDontNeedARealBehaivour thing;
// some methods...
}
So actually I need mock the aThingWhichDontNeedARealBehaivour but I don't know how. I tried to use an #Autowired on the #Spy service or try to inject mock on there but nothing works so I don't have more ideas about that.
P.D. I know the situation not the correct but actually I need the Spy service because I need check the data (its related with hashing, so I can't create a simulated behavior)
Related
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");
}
I am unable to understand few things while testing with JUnit, I have written a few tests, while some seem to work simply with
#RunWith(MockitoJUnitRunner.class)
and
#Mock
some seem not to work with them, and so I have to use
#RunWith(SpringRunner.class)
and
#MockBean
I understand that #MockBean is used when dealing with Spring Container, while #Mock is just to use to replicate/mock a certain class and its methods. But what would be the perfect time to use #MockBean?
#Test
public void addBulkFcmLog() throws Exception {
JacksonTester.initFields(this, new ObjectMapper());
AdminFcmResource adminFcmResource = AdminFcmResource.builder()
.adminId(123L)
.build();
given(this.fcmService.addBulkFcmLog(any(BulkFcmDataResource.class))).willReturn(adminFcmResource);
MockHttpServletResponse response = mockMvc.perform(
post(Routes.SEND_FCM + "/admin/logs/add")
.contentType(MediaType.APPLICATION_JSON)
.content(bulkFcmDataResourceJacksonTester.write(BulkFcmDataResource.builder().adminId(123L).build()).getJson()))
.andExpect(status().isCreated())
.andReturn()
.getResponse();
assertThat(response.getContentAsString()).isEqualTo(
adminFcmResourceJacksonTester.write(adminFcmResource).getJson()
);
assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value());
}
Above is a test I wrote for a controller in my spring boot application, but When i mock the fcmService class using #Mock, the response comes with an empty body, but then I changed It to #MockBean and Autowired the controller class then test returned the right result.
How did this happen?
In an integration-test context it is always advisable to work with beans (mocked if necessary). Few points to keep in mind:
#MockBean takes care of injecting the object, you do not need to set it yourself.
If your configuration is wrong (you did not specify a #Qualifier for example), the test will expose that problem on startup.
In an integration test you want to have your system in a state which resembles the prod scenario as close as possible, and #MockBean gets you closer than a plain #Mock
Many times the bean dependencies have no setters and manual injection would be just plain hard.
Well, i am very confused about #Spy and #Mock. In my understand #Spy will call real methods and #Mock/#InjectMocks don't, because it just a mock, then i need a stub (when.thenReturn) if i would like to change the behavior of a mock.
In my test class i have this code:
#RunWith(MockitoJUnitRunner.class)
public class CaixaServiceTest {
#InjectMocks
private CaixaService caixaService;
#Mock
private CaixaRepository caixaRepository;
So, CaixaRepository is a JpaRepository interface from Spring Data and CaixaService just have a very simple method:
public void calcular(){
int a = (int) Math.pow(1,3);
log.info(a);
}
If i call caixaRepository.findOne(id) null should be returned because findOne is never called really, because it just a mock. This case works very well.
But when i call caixaService.calcular() the method body is executed (shouldn't because it is a mock), so log.info(a) is logged on my file.
I can't understand this behavior, because as i said in my understand #InjectMocks or #Mock shouldn't execute anything if stub not exists, this a #Spy task.
All is right but your understanding of #InjectMocks.
Indeed annotating a field with it will not create a mock object as you think.
Instead of, it will try to inject the mock dependencies to the object referenced by the field where the annotation is.
Note that this way of injecting the dependencies is not explicit and so doesn't document the dependencies to mock in your test.
Besides if the dependencies injection fails, Mockito will not report any failure.
I started testing Spring Boot 2.0.0 and I encountered a strange behaviour with Mockito 2.17.0 and JUnit 5.1.0.
From what I saw, the way to get the mocks injected into the desired bean is to use the new #ExtendWith annotation with the MockitoExtension class.
So, here's my test class:
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#Mock
private A a;
#Mock
private B b;
#InjectMocks
private MyService myService;
// The test methods are omitted
}
That seemed fine but I found that the mocks didn't get called as expected and I figured out that this was due to a different instance of a and b inside the test class and the service itself.
Actually, it's because of the MockitoExtension being applied twice and the second time it is applied, the myService field isn't evaluated to null (obviously) which imply that the newly created mocks (a and b) aren't set to the current myService instance or a new one either.
Am I forgetting something?
I assume I could handle the mocks myself but I think that it isn't the purpose of the InjectMocks annotation.
Thank you for your time.
It looks like you hit Mockito issue: mockito#1346.
It's already fixed, so you may wait for a public release or use dev build 2.17.2: https://bintray.com/mockito/maven/mockito-development/2.17.2 (release notes)
I am trying to mock a local object, using Mockito Framework, that is being created from the spring application context; But every time I run the application its fails to replace the original object with the mocked object.
Here is the original class's code spinets:
public void executeMyJob(){
ApplicationContext springContext = ApplicationContextUtil.getApplicationContext();
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
List<Brand> dataList =music.getAll();
......
}
I want to mock the MusicEAO so when the getAll() method is called, it will use the mock object.
Below is my test class code snippets:
#Mock
MusicEAO musicEAO;
when(musicEAO.findAll()).thenReturn(myDefinedList);
How can I resolve this issue?
It's hard to tell from the cod you posted but the problem might be that you are mocking MusicEAO in your test but the code you are executing is using a Spring ApplicationContext to get a reference to the MusicEAO bean.
Your original class should not use MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO"); but instead have the bean injected by Spring using #Autowired through a constructor or a setter method (or other dependency injection method). You test will then be able to easily inject a mock version.
class MyJobClass {
MusicEAO music;
#Autowired
public MyJobClass(MusicEAO musicEao) {
this.music = musicEao;
}
public void executeMyJob(){
List<Brand> dataList =music.getAll();
......
}
}
When you say
every time I run the application it fails to replace the original
object with the mocked object
You shouldn't have to run the application to run a unit test for this class - are you asking how to inject mocks into a running application?
.
It doesn't work this way. In your current code :
The following instance is the one in your test :
#Mock MusicEAO musicEAO;
But in your production code, you are using Spring to acquire the Music instance :
MusicEAO music= (MusicEAO) springContext.getBean("MusicEAO");
Nowhere you seem to say to spring that you want the Music mock to be affected to MusicEAO bean name.
If you are doing a Unit Test I would recommand you to avoid messing with Spring, if that's an integration test, then you'll have to find a way to create the mock and pass it over to spring.
For example you can create the mock in the Spring context, autowire it in your test.
Also, I wouldn't recommand the use of static calls in this situation to acquire the Spring context in order to finaly get a hold on the Music object. it makes me think of Spring as a registry which isn't really the case. Spring is a container. Instead you should try to refactor your code in such a way that the Music bean is wired (#Autowired, setters, etc.) in ExecutionJob.
Then it would be even easier to write a Unit Test with JUnit and Mockito, with annotations like #Mock and #InjectMocks.
Hope that helps.