Clarification about #Spy and #InjectMocks inside a #Service Spring Boot - java

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.

Related

How give an auto wired inside a #Spy?

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)

Mockito instead of stubbing the method, is invoking the method

I am basically a new bee in using Mockito framework.
#Test
#SuppressWarnings("rawtypes")
public void testGetCaseDetailResponse() throws Exception {
HashMap requestM = new HashMap<String, String>();
requestM.put("transactionId", "******");
requestM.put("clientSystem", "URW");
requestM.put("loginId", "JUSTINN");
Mockito.when(caseDetailsService.getSAPCaseDetail(Mockito.any(), requestM))
.thenReturn(sapCaseDetailResponse);
}
the below part of the code which should ideally stub the method caseDetailsService.getSAPCaseDetail is invoking the method. I ran on debug mode and verified that is the case.
Mockito.when(caseDetailsService.getSAPCaseDetail(Mockito.any(), requestM))
.thenReturn(sapCaseDetailResponse);
For more info on the initialization part
#RunWith(MockitoJUnitRunner.class)
public class CaseDetailsServiceTest {
#Mock
RestTemplate restTemplate;
#Mock
AuthUtil authUtil;
#Mock
HttpHeaders httpHeaders;
#Mock
private HttpServletRequest httpRequest;
#Mock
SapServiceClient sapServiceClient;
#Mock
DateConvertUtils dateConvertUtils;
#Mock
CaseConverter caseConverter;
#InjectMocks
CaseDetailsService caseDetailsService;
I might be missing something, any help would be really appreciated, thanks in advance!
Here:
Mockito.when(caseDetailsService
But:
#InjectMocks
CaseDetailsService caseDetailsService;
The point of #InjectMocks is to insert previously created mock objects into an instance of your production class under test.
In other words: caseDetailsService isn't a mock. Thus you can't use when(caseDetailsService...).
You see, when() is used to specify the behavior of a Mockito created mock object. You can't apply when() on something that isn't a mock.
Thus, the real answer here: step back, and read a good tutorial about Mockito, and what its annotations really mean. Mocking frameworks are complicated, you can't learn them "trial and error"!
A good starting point: the tutorial at vogella.

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");
}

Integration test a manual transaction with transaction template

I am getting null pointer exceptions on my transaction template when I try to test my method that uses manual transactions. When I am running the application in Spring Boot it works as expected.
#Autowired
TransactionTemplate template;
public CompletableFuture<MyResultEntity> addToA(BInput input) {
return CompletableFuture
.supplyAsync(
() -> template.execute(status -> {
A a = aRepository.findOne(input.getA());
List<B> addedBs = saveBs(input.getB(), a);
return new MyResultEntity(a, addedBs);
}), MyCustomExecutor());
}
I have tried using a mock template and inject it like this:
#Mock
private TransactionTemplate transactionTemplate;
#InjectMocks
private MyClass myClass;
I have also tried annotating my test with:
#RunWith(SpringJUnit4ClassRunner.class)
When debugging this configuration the template is actually injected and is not null any more. But since I am interested in testing the actions in the transactions I do not wish to mock it so i use:
when(transactionTemplate.execute(Mockito.any())).thenCallRealMethod();
This throws a new null pointer exception though since the transaction template tries to use the TransactionManager and that is still null.
How can I unit test my method calls inside the the transaction template?
What I normally do is not to call the real method, but to just mock the real behavior instead. Calling the real method in the mock will fail, because a mock is not managed inside of springs injection context. Well, to be exact, you can make them exist inside the injection context by adding them to a test configuration (plain SpringMVC) or using #MockBean (spring boot). But still they are just injected as a dependency. But won't receive any dependencies. For unit tests this is most often the desired behavior.
So just do something like:
when(_transactionTemplate.execute(any())).thenAnswer(invocation -> invocation.<TransactionCallback<Boolean>>getArgument(0).doInTransaction(_transactionStatus));
_transactionStatus can be a mock itself to test the usage of the state inside the callback.
Mocking is what mocks are used for :)
If TransactionManager is null it means that Spring probably didn't load all necessary dependencies in the test context.
Anyway, why mocking the TransactionTemplate if you need to invoke the execute() method ?
Your test looks like an integration/sociable test and not an unit test.
If it is the case, you don't need to mock anything.
If you want to write an unit test that tests the actual logic in the addToA() method, you should use mock without partial mocking.
Mock dependencies used in the Supplier provided and assert that the expected MyResultEntity instance is returned.
Note that your unit test will have a limited value and may be considered as brittle as it asserts only that a series of method was invoked. Generally you want to assert a behavior in terms of more concrete logic such as computation/extraction/transformation.
Here is an example (no tested but it should give an idea on the way) :
#Mock
Repository ARepositoryMock;
#Mock
Repository BRepositoryMock;
#Test
public void addToA() throws Exception {
BInput input = new BInput();
// record mock behaviors
A aMockByRepository = Mockito.mock(A.class);
List<B> listBMockByRepository = new arrayList<>();
Mockito.when(ARepositoryMock.findOne(input.getA())).thenReturn(aMockByRepository);
Mockito.when(BRepositoryMock.saveBs(input.getB(), aMockByRepository)).thenReturn(listBMockByRepository);
// action
CompletableFuture<MyResultEntity> future = myObjectUnderTest.addToA(input);
//assertion
MyResultEntity actualResultEntity = future.get();
Assert.assertEquals(aMockByRepository, actualResultEntity.getA());
Assert.assertEquals(listBMockByRepository, actualResultEntity.getListOfB());
}

Mockito injects mocks twice with JUnit 5

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)

Categories

Resources