Integration test a manual transaction with transaction template - java

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

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

SpringBoot Container in Junit Test

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.

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

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.

unit testing mock injection

I am trying to unit test my class, and mock the DAO to provide predictable results. Still, it would appear that my DAO's method is still being called as I receive a hibernate error.
org.hibernate.exception.GenericJDBCException: Station TST not found.
This error is a result of my database not containing TST, but shouldnt this not even be called since I mocked the DAO? How do I mock the call so that the database isn't even hit.
Here's how I set up my mock
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class MyServiceTest{
#Autowired
private MyService service;
private MyDAO dao;
private LinkedList objs;
#Before
public void init() throws SecurityException, NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
// mock the dao for predictable results
dao = mock(MyDAO.class);
when(dao.myDBCall(anyString(), any(Date.class))).thenReturn(legs);
Field f = MyService.class.getDeclaredField("dao");
f.setAccessible(true);
f.set(service, dao);
}
#Test
public void testCall() {
// construct our sample leg data
legs = new LinkedList();
//fill in my stub data i want to return
Collections.shuffle(legs);
List results = service.testingCall("TST", new Date()); // this fails because service is using the dao, but it is making a call to the DB with this garbage 'Test' param rather than just returning 'legs' as stated in my when clause
assertThat(results, not(nullValue()));
}
#Test
public void testGetGates() {
// fail("Not yet implemented");
}
#Test
public void testGetStations() {
// fail("Not yet implemented");
}
}
I think that your Service is instantiated by spring with your Dao defined in your application-context.xml and you get your error before you even try to inject your mock in your service.
One thing I like to use to test my services is an Autowired constructor and then in my tests I do not instantiate my services by spring but using the constructor and the mock.
private MyDao myDao;
#Autowired
public MyService(MyDao myDao){
this.myDao = myDao;
}
And then in your test :
MyService myService = new Myservice(mockedDao);
For first, instead of Field f = MyService.class.getDeclaredField("dao"); check for PowerMock (check for Whitebox)
Mocking one object will not prevent hibernate to start (because you are loading applicationContext.xml, so hibernate will try to connect to DB). Your mock - it is just a POJO at one field of one test instance, not a Spring bean or replacement for class.
If you want to test only dao and service classes (create clear unit test) - remove Spring related annotations from test and do it on your own (create mock, create service, put mock to service, and test it).
If you want test your application with Spring context (create integration test) - create H2 in-memory database for hibernate and just test your service (don't forget to clear it in #After).
Third way - split your configuration file into two Spring profiles (for example dev and test) and implement mock yourself (put mock to test, real hibernate and dao to dev).
If you dont want use Spring profiles - it is possible to split applicationContext.xml into 3 files (for common beans, for real DB beans, for mocks).
And one more sexy way - use springockito-annotations (but you still need to avoid hibernate be loaded)

Mocking local object created by spring application context using Mockito

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.

Categories

Resources