How to mock Spring's JdbcTemplate.queryForList using Mockito? - java

I want to know how to mock the particular code using Mockito:
List<Map<String, Object>> list = jdbcTemplate.queryForList(
sqlQuery,
new Object[] { inflowId }
);
I tried the following code:
Mockito.doReturn(list)
.when(jdbcTemplate)
.queryForList(Mockito.anyString(), Mockito.any(Class.class));
and:
when(
jdbcTemplate.queryForList(Mockito.anyString(), Mockito.any(Object[].class))
).thenReturn(list);
My problem is that particular method is not getting mocked in JUnit. When the method is called, it returns null whereas it should return the list.

This should work:
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DemoTest {
#Test
public void mockJdbcTemplate() {
JdbcTemplate mockTemplate = Mockito.mock(JdbcTemplate.class);
List<Map<String, Object>> mockResult = new ArrayList<>();
Mockito.when(mockTemplate.queryForList(Mockito.anyString(), ArgumentMatchers.<Object>any())).thenReturn(mockResult);
// Alternatively:
// when(mockTemplate.queryForList(anyString(), Mockito.<Object>any())).thenReturn(mockResult);
String query = "some query";
Object[] params = new Object[]{1};
List<Map<String, Object>> returnedResult = mockTemplate.queryForList(query, params);
Assert.assertThat(returnedResult, CoreMatchers.sameInstance(mockResult));
}
}
The trick is to use ArgumentMatchers.<Object>any() as there are multiple queryForList method implementations and the one we want to mock receives a varargs parameter.

Following code i used with spring boot, mockito
/** class on which uni test is driven **/
public class Decompile {
#Autowired
private JdbcTemplate jdbcTemplate;
public List<Map<String, Object>> getRunner()
{
try{
return jdbcTemplate.queryForList("select * from users");
}
catch (Exception e) {
System.err.println(e);
}
return null;
}
}
Unit test case starts
/** Unit test case for above class **/
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.JdbcTemplate;
#RunWith(MockitoJUnitRunner.class)
public class DecompileTest {
#Mock/* works fine with autowired dependency too */
JdbcTemplate jdbcTemplate;
#InjectMocks/* for the claa that we are mocking */
Decompile testclass;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testgetRunner() {
List<Map<String, Object>> expectedresultList = new ArrayList<>();
Mockito.lenient().when(jdbcTemplate.queryForList("select * from users.. ")).thenReturn(expectedresultList);
List<Map<String, Object>> response = testclass.getRunner();
}
}
mvn package used as following (compatible with spring version > 2)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

Related

Why does #InjectMocks in Java Spring return null in my test case for a service?

I am trying to write test cases for a service I have implemented for a controller in Spring. I have the following Service and Test classes.
StudentCourseRequestService:
import java.util.List;
import org.springframework.stereotype.Service;
import model.CourseRequest;
import repository.ICourseRequestRepository;
import lombok.RequiredArgsConstructor;
#Service
#RequiredArgsConstructor
public class StudentCourseRequestService implements IStudentCourseRequestService {
private final ICourseRequestRepository courseRequestRepository;
#Override
public boolean requestCourse(CourseRequest courseRequest) {
return courseRequestRepository.saveRequest(courseRequest);
}
#Override
public List<CourseRequest> getAllCourseRequests() {
return courseRequestRepository.getAllCourseRequests();
}
#Override
public List<CourseRequest> getAllCourseRequestsOfStudent(Long studentId) {
return courseRequestRepository.getCourseRequestsByStudentId(studentId);
}
}
Test class:
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
public class StudentCourseRequestServiceTests {
#MockBean
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}
When I run the requestValidCourse() test case, I get the following error:
java.lang.NullPointerException: Cannot invoke "repository.ICourseRequestRepository.saveRequest(model.CourseRequest)" because "this.courseRequestRepository" is null
at service.StudentCourseRequestServiceTests.requestValidCourse(StudentCourseRequestServiceTests.java:29)
at java.base/java.util.ArrayList.forEach(Unknown Source)
at java.base/java.util.ArrayList.forEach(Unknown Source)
How can I resolve this issue?
As #Lesiak recommended, I added the #ExtendWith(MockitoExtension.class) annotation to the top of the class, and changed courseRequestRepository to have the #Mock annotation, instead of the previous #MockBean annotation. The issue got resolved after these changes.
Previously I had the configuration recommended by #Lesiak during my trials to resolve the issue, but I also had the annotation #ExtendWith(SpringExtension.class) at the top of the class. With this configuration, I got the same error. Removing the #ExtendWith(SpringExtension.class) annotation resolved the issue here.
New test class:
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import model.CourseRequest;
import repository.ICourseRequestRepository;
#ExtendWith(MockitoExtension.class)
public class StudentCourseRequestServiceTests {
#Mock
private ICourseRequestRepository courseRequestRepository;
#InjectMocks
private StudentCourseRequestService studentCourseRequestService;
#Test
public void requestValidCourse() throws Exception {
final CourseRequest courseRequest = new CourseRequest(
"data0",
"data1",
"data2",
"data3",
"data4",
"data5"
);
Mockito.when(courseRequestRepository.saveRequest(courseRequest)).thenReturn(true);
boolean result = studentCourseRequestService.requestCourse(courseRequest);
assertThat(result).isTrue();
}
}

Error in Junit testcases for a contoller file

I am new to spring boot and Junit. I am writing a small Junit testcase for my controller.
My controller file looks like below:
#RequestMapping(path = "/project-files")
public class MyController {
private RequestModel setRequestModel(String params) {
RequestModel requestModel = new RequestModel();
requestModel.setRequestData(params);
return requestModel;
}
#PostMapping("/deleteUploadedFile")
#ResponseBody
public Map<String, Object> deleteUploadedFile(#RequestParam(value = "requestData", required = false) String params) {
RequestModel requestModel = setRequestModel(params);
return service.deleteUploadedFile(requestModel);
}
}
Based on the reference from here I tried to write a junit tescases for my controller.
My Junit Testcases looks like below:
package com.test.project.web;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.test.project.beans.RequestModel;
import com.test.project.service.serviceImpl;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
public class MyControllerTest<mapFromJson> {
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
#InjectMocks
MyController myController;
private MockMvc mockMvc;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new MyController()).build();
}
protected <T> T mapFromJson(String json, Class<T> clazz) throws
JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper(); return
objectMapper.readValue(json, clazz);
}
#Test
public void deletefile() throws Exception {
String uri = "/deleteUploadedFile";
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(uri).accept(MediaType.APPLICATION_JSON_VALUE))
.andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
MyController[] productlist = super.mapFromJson(content, MyController[].class);
assertTrue(productlist.length > 0);
}
}
I am getting errors
java.lang.IllegalStateException: Failed to load ApplicationContext
Is there any other better way to write the Junit testcases for controller?
#Test public void deletefile() throws Exception {
MvcResult mvcResult = mockMvc.perform(post("/project-files/deleteUploadedFile") //Getting error in post.
.param("params", "some_value_here"))
.andExpect(status().isOk()); //Getting error in status
String content = mvcResult.getResponse().getContentAsString();
productlist = super.mapFromJson(content, MrmController[].class); //Getting error in productlist & mapFromJson
assertTrue(productlist.length > 0);
}
You can use the .andExpect() method to validate the status of the request instead of using assertEquals()
You can use something in the lines of this to test your endpoint call:
mockMvc.perform(post("/project-files/deleteUploadedFile")
.param("params", "some_value_here"))
.andExpect(status().isOk());
Some strange things I find inside of your controller is that you use #PostMapping for deleting you can use #DeleteMapping instead see spring docs here
Also, you should use more descriptive names for parameter names (and in general)
String params
don't really ring a bell as to what this variable is representing.
If the issue persists make sure to post the entire stacktrace as suggested.
*Keep in mind when you're writing tests in general you are mimicking the behaviour of the real call, so don't add useless checks or mocks
#InjectMocks
private serviceImpl serviceImpl;
#InjectMocks
dataRepoImpl repoImpl;
I don't see the above in your controller so I am not sure why are they part of the test.

How to Use FakeMongo and Ignore MongoDB socket connection on Controller level Integration testing

I am using spring boot 1.5.x for application. Database is MONGO
Using MongoRepository for crud operations.
Earlier in our project we got dedicated test database instance so we have added mongodb properties of test db in src\test\resources\application.properties.
Now we dont have test db so we want to use embedded/fake db to test our classes Controller/Services/Repository etc..
Options are - embeded mongodb like flapdoodle and fongo
I tried 1 of solution de.flapdoodle.embed.mongo from SO question to use flapdoodle
de.flapdoodle.embed.mongo trying to download zip outside network : unable-to-download-embedded-mongodb-behind-proxy-using-automatic-configuration
I tried fakemongo but its not working for unit and integration testing of controller. Its picking mongo database details of test db using application.properties present in test/resources
pom.xml
<dependency>
<groupId>com.github.fakemongo</groupId>
<artifactId>fongo</artifactId>
<version>${fongo.version}</version>
<!--<scope>test</scope> -->// not working, if removed it says cannot find mongo
</dependency>
<dependency>
<groupId>com.lordofthejars</groupId>
<artifactId>nosqlunit-mongodb</artifactId>
<version>0.7.6</version>
<scope>test</scope>
</dependency>
FakeMongo Configuration
package com.myapp.config;
import com.github.fakemongo.Fongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
#Configuration
#EnableMongoRepositories
public class FakeMongo extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
return "mockDB";
}
#Bean
public MongoClient mongo() {
Fongo fongo = new Fongo("mockDB");
return fongo.getMongo();
}
}
TestFakeMongo It works. Test executed properly - Reference http://springboot.gluecoders.com/testing-mongodb-springdata.html
package com.myapp.config;
import com.myapp.domain.entitlement.User;
import com.myapp.repository.UserRepository;
import com.lordofthejars.nosqlunit.annotation.UsingDataSet;
import com.lordofthejars.nosqlunit.core.LoadStrategyEnum;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.junit.Assert.assertTrue;
#RunWith(SpringRunner.class)
#Import(value = {FakeMongo.class})
public class TestFakeMongo {
#Autowired
private ApplicationContext applicationContext;
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#MockBean
private UserRepository userRepository;
#Test
#UsingDataSet(loadStrategy = LoadStrategyEnum.DELETE_ALL)
public void getAllUsers_NoUsers() {
List<User> users = userRepository.findAll();
assertTrue("users list should be empty", users.isEmpty());
}
}
UserRepositoryTest - Unit testing for Repository also working using fongo Reference - http://dontpanic.42.nl/2015/02/in-memory-mongodb-for-unit-and.html
package com.myapp.repository;
import com.myapp.config.SpringUnitTest;
import com.myapp.domain.User;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class UserRepositoryTest extends SpringUnitTest {
#Autowired
private UserRepository userRepository;
#Before
public void setup() {
importJSON("user", "user/user.json");
}
#Test
//#UsingDataSet(loadStrategy = LoadStrategyEnum.CLEAN_INSERT, locations = "/json-data/user/user.json") // test/resources/..
public void findUser_should_return_user() {
User user = userRepository.findByXYZId("XX12345");
assertNotNull(user);
}
#Test
public void findUser_should_return_null() {
User user = userRepository.findByXYZId("XX99999");
assertNull(user);
}
#Test
public void deleteUser_should_return_null() {
userRepository.delete("XX12345");
User user = userRepository.findByXYZId("XX12345");
assertNull(user);
}
#Test
public void saveUser_should_return_user() {
User user = new User();
user.setXYZId("XX12345");
user.setAck(true);
List<DateTime> dateTimeList = new ArrayList<>();
dateTimeList.add(DateTime.now(DateTimeZone.UTC));
user.setAckOn(dateTimeList);
User dbUser = userRepository.save(user);
assertEquals(user, dbUser);
}
#Test
public void findAllUser_should_return_users() {
List<User> userList = userRepository.findAll();
assertEquals(1, userList.size());
}
}
Controller level testing not working.. Reference- https://www.paradigmadigital.com/dev/tests-integrados-spring-boot-fongo/
UserControllerTest failed java.lang.IllegalStateException: Failed to load ApplicationContext - At this point test mongo db details fetech instead of fongo test\resources\application.properties test server db details are loading
package com.myapp.config;
package com.myapp.controllers;
import com.myapp.config.FakeMongo;
import com.myapp.services.persistance.UserService;
import com.lordofthejars.nosqlunit.mongodb.MongoDbRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import static com.lordofthejars.nosqlunit.mongodb.MongoDbRule.MongoDbRuleBuilder.newMongoDbRule;
import static org.hamcrest.CoreMatchers.containsString;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#ActiveProfiles("it")
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = Application.class
)
#Import(value = {FakeMongo.class})
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:application-it.properties")
public class UserControllerTest {
#Rule
public MongoDbRule embeddedMongoDbRule = newMongoDbRule().defaultSpringMongoDb("mockDB");
#Autowired
private MockMvc mockMvc;
#MockBean
private UserService service;
#Before
public void setUp() throws Exception {
}
#Test
public void deleteUser() throws Exception {
Mockito.when(this.service.deleteUser(Mockito.anyString())).thenReturn(true);
this.mockMvc.perform(delete("/User")).andDo(print()).andExpect(status().isOk())
.andExpect((ResultMatcher) content().string(containsString("true")));
}
}
UserControllerTest Not working, failing with error java.lang.IllegalStateException: Failed to load ApplicationContext as its tries to connect to test instance of mongo database using application.properties present in test/resources
Appreciate working example to use fakemongo while running Integration test for Controller level
What changes, I need to do in code level(Controller class or any other class) or application.properties of test\resources ?

How to mock Injected value relying on a qualifier using JerseyTest

I'm trying to mock a controller/resource including the jax-rs layer. The class has dependencies that need to be injected.
It however also has some String values that are injected using a qualifier interface.
Basically, I'm using JerseyTest to run a single controller and use HK2 for dependency injection. I created a ResourceConfig and registered a AbstractBinder to bind the injected classes.
This works fine for regular injected dependencies, but when the the additional #SomeQualifierInterface annotation is added, it crashes with the following error:
MultiException stack 1 of 3
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=String,parent=ThingsController,qualifiers={#com.company.SomeQualifierInterface()},position=-1,optional=false,self=false,unqualified=null,10035302)
...
MultiException stack 2 of 3
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.company.ThingsController errors were found
...
MultiException stack 3 of 3
java.lang.IllegalStateException: Unable to perform operation: resolve on com.company.ThingsController
...
See the simplified full code example below:
Controller / Resource
import org.slf4j.Logger;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
#Path("/things")
public class ThingsController {
#Inject
private Logger log;
#Inject
#SomeQualifierInterface
private String injectedQualifierValue;
#GET
public Response getThings() {
log.info("getting things");
System.out.println("Injected value: " + injectedQualifierValue);
return Response.status(200).entity("hello world!").build();
}
}
Qualifier interface
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RUNTIME)
#Target({ TYPE, METHOD, FIELD, PARAMETER })
public #interface SomeQualifierInterface { }
Producing service
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Produces;
#ApplicationScoped
public class SomeProducerService {
#Produces
#Dependent
#SomeQualifierInterface
public String getQualifierValue() {
return "some value!";
}
}
Test
import org.glassfish.jersey.internal.inject.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
#Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
#Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(String.class); // Doesn't work
bind(new SomeProducerService()).to(SomeProducerService.class); // Doesn't work
}
});
return resourceConfig;
}
#Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
}
POM
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.27.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
<scope>test</scope>
</dependency>
Solved!
First, use the AbstractBinder from org.glassfish.hk2.utilities.binding.AbstractBinder instead of org.glassfish.jersey.internal.inject.AbstractBinder.
Second, create a class that extends AnnotationLiteral and implements the interface.
Last, bind the value to a TypeLiteral and set the qualifiedBy to the AnnotationLiteral.
Full code:
import org.glassfish.hk2.api.AnnotationLiteral;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.slf4j.Logger;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
public class MockedThingsControllerTest extends JerseyTest {
private Logger logMock = mock(Logger.class);
#Override
protected Application configure() {
ResourceConfig resourceConfig = new ResourceConfig(ThingsController.class);
resourceConfig.register(new AbstractBinder() {
#Override
protected void configure() {
bind(logMock).to(Logger.class);
bind("some mocked value").to(new TypeLiteral<String>() {}).qualifiedBy(new SomeQualifierLiteral());
}
});
return resourceConfig;
}
#Test
public void doSomething() {
Response response = target("/things").request().get();
assertEquals(200, response.getStatus());
verify(logMock).info("getting things");
}
static class SomeQualifierLiteral extends AnnotationLiteral<SomeQualifierInterface> implements SomeQualifierInterface {}
}

How to verify arguments passed to a static function with Java JUnit and Mockito (Spring Boot)

I'm new to all the mentioned technologies so it might be a stupid question.
We have a spring boot application where we need to write to a PostgreSQL-Database via JDBC.
Therefore we need the static DriverManager.getConnection() method to open the connection.
Now in my unit tests I don't want to call this class directly.
Instead I want to check, that the DriverManager.getConnection() is called with the correct String as that is my expected observable external behavior.
I encapsulated this behavior into a ConnectionFactory with the method newConnection(ConnectionType.POSTGRESQL) because we got more than one Database to use in this Application.
Now I can't find a way to verify via Mockito that this external dependency was called with the correct String like you could with an instance:
DriverManager dm = mock(DriverManager);
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verify(dm).getConnection("theConnectionStringToBeExpected");
So how to do this with the static dependency?
I tried the Captor-way but this seems to only work for direct usage like
mockStatic(DriverManager.class);
final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");
// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verifyStatic();
StaticService.getConnection(captor.capture());
assertEquals("theExpectedConnectionString", captor.getValue());
Edit:
Here is the nasty little workaround which I currently use for another server...
public void driverManagerIsCorrectlyCalledForAds() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
final Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);
assertEquals(expectedConnection, actualConnection);
}
Edit 2:
TestClass:
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {
#InjectMocks
ConnectionFactory connectionFactory;
#Mock
DatabaseDriverInformation databaseDriverInformation;
#Mock
DatabaseProperties databaseProperties;
#Mock
DatabaseProperties.Pg pg;
#Mock
DatabaseDriverLoader databaseDriverLoader;
#Before
public void setUp() {
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(ads).getServer();
doReturn(1338).when(ads).getPort();
doReturn("database_name").when(ads).getDatabasename();
doReturn("user").when(ads).getUser();
doReturn("password").when(ads).getPassword();
}
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);
assertEquals(expectedConnection, actualConnection);
}
}
Class under Test:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
#Service()
public class ConnectionFactory {
#Autowired
private DatabaseDriverLoader databaseDriverLoader;
#Autowired
DatabaseProperties databaseProperties;
public Connection newConnection(ConnectionType connectionType) {
databaseDriverLoader.load();
final String connectionString = connectionStringFor(connectionType);
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException sqlException) {
throw new RuntimeException("Couldn't connect to Server");
}
}
private String connectionStringFor(ConnectionType connectionType) {
switch (connectionType) {
case ADS:
return this.adsConnectionString();
case POSTGRESQL:
return this.pgConnectionString();
default:
throw new RuntimeException("Invalid connection Type requested!");
}
}
private String adsConnectionString() {
return new StringBuilder()
.append("jdbc:extendedsystems:advantage://")
.append(databaseProperties.getAds().getServer())
.append(":")
.append(databaseProperties.getAds().getPort())
.append("/")
.append(databaseProperties.getAds().getDatabasename())
.append(";user=")
.append(databaseProperties.getAds().getUser())
.append(";password=")
.append(databaseProperties.getAds().getPassword())
.append(";chartype=ansi")
.toString();
}
private String pgConnectionString() {
return new StringBuilder()
.append("jdbc:postgresql://")
.append(databaseProperties.getPg().getServer())
.append(":")
.append(databaseProperties.getPg().getPort())
.append("/")
.append(databaseProperties.getPg().getDatabasename())
.append("?user=")
.append(databaseProperties.getPg().getUser())
.append("&password=")
.append(databaseProperties.getPg().getPassword())
.toString();
}
}
I removed the package-names, some specific imports and some unnecessary tests which are working.
After some search I found this: How to verify static void method has been called with power mockito
In Essence:
first:
mockStatic(ClassWithStaticFunctionToVerify.class)
second:
Execute the Code that will call the function you want to verify later
third:
verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
With it's help I got the following Solution which works fine:
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
// Arrange
mockStatic(DatabaseProperties.Pg.class);
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(pg).getServer();
doReturn(1338).when(pg).getPort();
doReturn("database_name").when(pg).getDatabasename();
doReturn("user").when(pg).getUser();
doReturn("password").when(pg).getPassword();
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).loadAdsDriverClass();
doNothing().when(databaseDriverLoader).loadPgDriverClass();
when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);
// Act
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
// Assert
verifyStatic();
DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}

Categories