Verifying internal proxy calls in Mockito - java

How to verify internal proxy calls in unit testing by using Mockito framework?
I am trying to write a test for doAllTasks() method and verify that doSingleTask() was called a certain amount of times. Obviously I can not split my service into two because these methods have the same meaning. The simplest solution is to add setProxy() method but disadvantage of this is that I will need to add test related code to my service definition. Any ideas?
#Service
public class XXXServiceImpl implements XXXService, BeanNameAware {
private String name;
private XXXService proxy;
#Autowired
private ApplicationContext applicationContext;
#Override
public void setBeanName(String name) {
this.name = name;
}
#PostConstruct
public void postConstruct() {
proxy = (XXXService)applicationContext.getBean(name);
}
#Transactional
public void doAllTasks(){
for(...)
proxy.doSingleTask(...);
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void doSingleTask(...){
}
}

A simple way to fix this is to add a setter for the proxy and overwrite the field in the unit test with a simple mock. If you also add a getter, you can restore it after the test.
The main drawback is that this prevents you from running the tests in parallel unless you create a new ApplicationContext for this test.
An alternative might be to give the field a #Qualifier so you can define two different beans in your test's configuration. In production, you simply return the singleton.

Inject mock of ApplicationContext.
Mock getBean meatod to return mock of XXXService
Verify doSingleTask invoked

Related

Java: calling public transactional method from a private method

I have two classes
public class MyTest {
#Autowired
private MyService myService;
private void test() {
myService.writeToDb();
}
}
#Service
public class MyService {
#Transactional
public void writeToDb() {
// do db related stuff
}
}
I want to know if calling a method test() (which is a private method) from MyTest class would create a transaction.
P.S
I'm using Spring Boot. And Java 17.
It will work, whether you call the method of another object from a public or private method inside yours is an implementation detail. From callee's point of view, it's the same, it is not even aware of the caller's context.
Spring AOP uses the Proxy pattern to handle those scenarios. It means you are not directly receiving a MyService bean, but a MyServiceSpringCreatedProxy (not the actual name, check in debug mode and you'll see), which is actually handling transactions around methods.
So as long as the call passes through the Spring's proxy, the #Transactional will be accounted for as expected. Bear in mind that it doesn't mean a new transaction is open, it depends if another already exists and your configuration.
However, any self call (to a public or a private method) would not pass through the proxy and then #Transactional would not be working.
#Service
public class MyService {
// can be private, public or whatever
public void callRelatedStuff() {
//self call, no transactional work done
writeToDb();
}
#Transactional
public void writeToDb() {
// do db related stuff
}
}

Mockito calls stubbed method even if using doReturn

I want to create an integration test where a put method is called on a controller and it updates a certain object. During this process, a service class is involved which calls a third party API to do some stuff. In my case, I want to stub the service method which is involved in calling the third party as it is not the point to test the third party.
Having that said, I will present my code and I wait for an answer about why this does not work as expected and/or any other workaround.
This is my service class in which is the method call I want to stub.
public class ProjectService implements SomeInterfance {
// the third party service
private final DamConnector damConnector;
// some other fields
public ProjectDTO save(ProjectDTO projectDTO) {
log.debug("Request to save Project : {}", projectDTO);
// some operations
synchronizeWithDamAndSave(project, parentChanging); //this is the method call I want to be skiped
//other operations
return projectMapper.toDto(project, this);
}
//the method that I want to stub
public Asset synchronizeWithDamAndSave(Project project, boolean includeDocuments) {
Asset asset = synchronizeWithDam(project, includeDocuments);
projectRepository.save(project);
return asset;
}
}
And my integration test class:
#SpringBootTest(classes = SppApp.class)
public class ProjectResourceIT {
//other fields
//my service use autowire as it needs to make the service calls
#Autowired
private ProjectService projectService;
//this is my setup method where I create the spy of project service and define the doReturn behavior when my method is called
#BeforeEach
public void setup() {
ProjectService spyProjectService = Mockito.spy(projectService);
Mockito.doReturn(new Asset()).when(spyProjectService).synchronizeWithDamAndSave(Mockito.any(Project.class),Mockito.anyBoolean());
MockitoAnnotations.initMocks(this);
final ProjectResource projectResource = new ProjectResource(spyProjectService, clientService, securityService);
this.restProjectMockMvc = MockMvcBuilders.standaloneSetup(projectResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService())
.setMessageConverters(jacksonMessageConverter)
.setValidator(validator).build();
}
}
...
public void updateProject() throws Exception {
// initialization of the test
// this is where I call my controller
restProjectMockMvc.perform(put("/api/projects")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(projectDTO)))
.andExpect(status().isOk());
}
}
The problem in my case is that mockito enters in synchronizeWithDamAndSave method just after
Mockito.doReturn(new Asset()).when(spyProjectService).synchronizeWithDamAndSave(Mockito.any(Project.class),Mockito.anyBoolean());
this line is called, before the method to be called from the rest api.
What should I do? Any hints about why this is happening?
Spring Boot's proxies are not working wit Mockito. Use #SpyBean instead of #Autowired.

Mocking with anonymous class in Java

I'm learning about dependecy injection and testing with Mockito. And I just found a tutorial where someone explain an application with dependency injection and without dependency injection. This is the link: https://www.journaldev.com/2394/java-dependency-injection-design-pattern-example-tutorial
I have 2 questions:
The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use #Mock to mock an object?
This is his code for testing:
public class MyDIApplicationJUnitTest {
private MessageServiceInjector injector;
#Before
public void setUp(){
//mock the injector with anonymous class
injector = new MessageServiceInjector() {
#Override
public Consumer getConsumer() {
//mock the message service
return new MyDIApplication(new MessageService() {
#Override
public void sendMessage(String msg, String rec) {
System.out.println("Mock Message Service implementation");
}
});
}
};
}
#Test
public void test() {
Consumer consumer = injector.getConsumer();
consumer.processMessages("Hi Pankaj", "pankaj#abc.com");
}
#After
public void tear(){
injector = null;
}
}
And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.
The first question is about the code that he writes for testing. What kind of mock is that? Don't you need to use #Mock to mock an object?
To mock an object you have to provide an object that has same type i.e behaves the same or is a subtype of the class that object you want to mock. So to mock an object you can use an instance of anonymous class (in your case it would be an object of anaonymous class that extends MyDIApplication) or you can use Mockito with its #Mock annotation which does basically similiar thing. Bascially using #Mock annotation is similiar to doing :
MyDIApplication myDiApplication = Mockito.mock(MyDIApplication.class)
which creates a mock object extending class passed in constructor. Here you may pass interface or class depending on what you want to mock.
When using anonymous class you provide implementation of methods that you want to mock in overriden implementations of methods, but in case of Mockito you provide intender behaviour by using methods like Mockito::when.
And the second question is about testing the app without dependency injection. I don't understand why he say that: "Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes." Why we cannot mock these objects in our test cases.
I guess you are refering to this piece of code :
public class MyApplication {
private EmailService email = new EmailService();
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Here you create an instance of EmailService as class field. So there is no possibilty you can mock this (although you could use reflection or PowerMock). So you are tightly coupled to EmailService and it is hard to test MyApplication class logic. To be able to test it you can use constructor injection :
public class MyApplication {
private EmailService email;
public MyApplication(EmailService emaliService) {
this.email = emailService;
}
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}
Or setter injection :
public class MyApplication {
private EmailService email;
public void setEmailService(EmailService emailService) {
this.email = emailService;
}
public void processMessages(String msg, String rec){
//do some msg validation, manipulation logic etc
this.email.sendEmail(msg, rec);
}
}

Mock not used with #Secured

My mocks are not being picked up when I add a #Secured to any method in the service class and test the service class. This even happens when I am testing a method that isn't secured. I don't understand whether it is due to the mock not being used or being overriden.
public class Service {
#Autowired
private DAO dao;
public void method1() {
...
//System.out.println("DEBUG:" + dao.hashCode());
dao.XXX();
...
}
#Secured("...") // added second time (Case 2)
public void method2() {
...
dao.XXX();
...
}
}
public class ServiceTest {
#Mock
private DAO dao;
#InjectMocks
#Autowired
private Service service;
#Before
public void beforeTest() {
MockitoAnnotations.initMocks(this);
}
public void testMethod1() {
Mockito.when(dao.XXX()).thenReturn(...);
//System.out.println("DEBUG:" + dao.hashCode());
...
}
public void testMethod2() {
Mockito.when(dao.XXX()).thenReturn(...);
...
}
}
Case 1:
I test method1 (testMethod1) without the #Secured on method2 and everything works fine. My expected results match what the Mock dao returns.
Case 2
I add the #Secured to method2 and re-run testMethod1. The test completes but the results are not matching the Mock dao. The actual dao has been used and the actual results match the database data.
I use the DEBUG statements to print the hashcode of the dao in the ServiceTest class and the Service class. In Case 1 they are the same. In Case 2 they are different.
Apparently there is something happening when I add the #Secured. I want to know what that is.
Assuming you are using the SpringJUnit4ClassRunner, the difference may be that Spring is creating a Proxy around your Service to handle the #Secured annotation, and Mockito cannot handle the injection of the dao in the proxy.
Btw, the same would happen with #Asynchronous, #Transactional and co.
I would suggest to use the MockitoRunner (forget Spring), or, if you really need to combine Spring and Mockito, use springockito

Mockito, Testing an object that relies on injected dependencies (Spring)?

I'm new to using Mockito and am trying to understand a way to make a unit test of a class that relies on injected dependencies. What I want to do is to create mock objects of the dependencies and make the class that I am testing use those instead of the regular injected dependencies that would be injected by Spring. I have been reading tutorials but am a bit confused on how to do this.
I have one the class I want to test like this:
package org.rd.server.beans;
import org.springframework.beans.factory.annotation.Autowired;
public class TestBean1 {
#Autowired
private SubBean1 subBean1;
private String helloString;
public String testReturn () {
subBean1.setSomething("its working");
String something = subBean1.getSomething();
helloString = "Hello...... " + something;
return helloString;
}
Then I have the class that I want to use as a mock object (rather than the regular SubBean1 class, like below:
package org.rd.server.beans.mock;
public class SubBean1Mock {
private String something;
public String getSomething() {
return something;
}
public void setSomething(String something) {
this.something = something;
}
}
}
I just want to try running a simple test like this:
package test.rd.beans;
import org.rd.server.beans.TestBean1;
import junit.framework.*;
public class TestBean1Test extends TestCase
{
private TestBean1 testBean1;
public TestBean1Test(String name)
{
super(name);
}
public void setUp()
{
testBean1 = new TestBean1();
// Somehow inject the mock dependency SubBean1Mock ???
}
public void test1() {
assertEquals(testBean1.testReturn(),"working");
}
}
I figure there must be some fairly simple way to do this but I can't seem to understand the tutorials as I don't have the context yet to understand everything they are doing / explaining. If anyone could shed some light on this I would appreciate it.
If you're using Mockito you create mocks by calling Mockito's static mock method. You can then just pass in the mock to the class you're trying to test. Your setup method would look something like this:
testBean1 = new TestBean1();
SubBean1 subBeanMock = mock(SubBean1.class);
testBean1.setSubBean(subBeanMock);
You can then add the appropriate behavior to your mock objects for whatever you're trying to test with Mockito's static when method, for example:
when(subBeanMock.getSomething()).thenReturn("its working");
In Mockito you aren't really going to create new "mock" implementations, but rather you are going to mock out the methods on the interface of the injected dependency by telling Mockito what to return when the method is called.
I wrote a test of a Spring MVC Controller using Mockito and treated it just like any other java class. I was able to mock out the various other Spring beans I had and inject those using Spring's ReflectionTestUtils to pass in the Mockito based values. I wrote about it in my blog back in February. It has the full source for the test class and most of the source from the controller, so it's probably too long to put the contents here.
http://digitaljoel.nerd-herders.com/2011/02/05/mock-testing-spring-mvc-controller/
I stumbled on this thread while trying to set up some mocks for a slightly more complicated situation and figured I'd share my results for posterity.
My situation was similar in the fact that I needed to mock dependencies, but I also wanted to mock some of the methods on the class I was testing. This was the solution:
#MockBean
DependentService mockDependentService
ControllerToTest controllerToTest
#BeforeEach
public void setup() {
mockDependentService = mock(DependentService.class);
controllerToTest = mock(ControllerToTest.class);
ReflectionTestUtils.setField(controllerToTest, "dependantService", mockDependentService);
}
#Test
void test() {
//set up test and other mocks
//be sure to implement the below code that will call the real method that you are wanting to test
when(controllerToTest.methodToTest()).thenCallRealMethod();
//assertions
}
Note that "dependantService" needs to match whatever you have named the instance of the service on your controller. If that doesn't match the reflection will not find it and inject the mock for you.
This approach allows all the methods on the controller to be mocked by default, then you can specifically call out which method you want to use the real one. Then use the reflection to set any dependencies needed with the respective mock objects.
Hope this helps someone down the road as it stumped me for a while.

Categories

Resources