Mock not used with #Secured - java

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

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.

Getting Missing 1 invocation to... in

I have a test that is testing a Spring #Service class. This service class autowires in a dao #Repository. The dao also autowires in a class to provide extra functionality. I would like to mock the calls in the dao so I have something like so:
#DisplayName("Tests for ...")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MyServiceTest
{
#Injectable
TheRepository myDao;
#Tested(fullyInitialized = true)
TheService myServiceUnderTest;
#Nested
#Tag("...")
#DisplayName("Tests for method: ...")
class TestClassContainer {
#Test
public void test1() {
IAnalysisDataScenario data = new CommonAnalysisDataScenario();
new Expectations() {{
myDao.nameExists(anyString); result = data.mockedNameExists();
...
}};
Map<String, Object> result = myServiceUnderTest.getAnalysis(data.getName(),data.getId());
assertTrue(!result.isEmpty());
}
}
}
The call to getAnalysis in the service calls the dao method nameExists which I want to return the result from mockedNameExists in the Expectations block but I can't figure out what I'm doing wrong.
This is what I see when I run the test:
Missing 1 invocation to:
...dao.impl.TheRepository#nameExists(any String)
on mock instance: ...dao.impl.TheRepository#61230f6a
Caused by: Missing invocations
at ...dao.impl.TheRepository.nameExists(TheRepository.java)
I see what the issue is in this case. The error message now makes sense. In this particular test, the method that is missing the invocation is not actually called because of the code path the test takes based on the the parameters. So the Expectations block must only contain mocking behavior for code that actually WILL BE EXECUTED. Now I understand

How to use #Before/#BeforeClass with #Autowired field

I have a test case which has an #Autowired field. I would like to have one method for setting up the test case, as it has many #Test-annotated methods that will rely on the same generated data, (for which I need the autowired class).
What's a good way to achieve this?
If I have the #BeforeClass, then I need to make the method static, which breaks the autowiring.
1st solution
Use TestNG instead.
#Before* annotations behave this way in TestNG.
No method annotated with #Before* has to be static.
#org.testng.annotations.BeforeClass
public void setUpOnce() {
//I'm not static!
}
2nd solution
And if you don't want to do that, you can use an execution listener from Spring (AbstractTestExecutionListener).
You will have to annotate your test class like this:
#TestExecutionListeners({CustomTestExecutionListener.class})
public class Test {
//Some methods with #Test annotation.
}
And then implement CustomTestExecutionListener with this method:
public void beforeTestClass(TestContext testContext) throws Exception {
//Your before goes here.
}
Self-contained in one file that would look like:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"commonContext.xml" })
#TestExecutionListeners({SimpleTest.class})
public class SimpleTest extends AbstractTestExecutionListener {
#Override
public void beforeTestClass(TestContext testContext) {
System.out.println("In beforeTestClass.");
}
#Test
public void test() {
System.out.println("In test.");
}
}
I came up with the solution of creating a separate initialization method (not setUp) annotated with #PostConstruct. This is not really an elegant solution, but it makes sure that the autowired/injected fields are properly initialized by Spring before using them, (which was the initial problem with the statically #BeforeClass annotated method).

Verifying internal proxy calls in Mockito

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

Categories

Resources