Writing a test to Spring boot REST API that retrieve data from a DB - spring

I have a spring boot REST API with a GET method that returns data available in a DB. I am attempting to write an integration test to test this API method. I have configured the test to use the H2 database. I am trying to add some mock data to the database before the test is executed and see if the API retrieves that data. Following is the code I have written so far.
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#TestPropertySource(locations = "classpath:application-test.properties")
public class MetaControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ProvinceDAO provinceDAO;
#Transactional
#Before
public void addData () {
Province southern = getProvinceEntity("Southern", "දකුණ", "தென்");
provinceDAO.createEntity(southern);
System.out.println(provinceDAO.findAll(Province.class).size());
}
#Test
public void testGetProvinces() throws Exception {
MvcResult result = mvc.perform(get("/meta/provinces"))
.andExpect(status().isOk())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
However, when I run this code, I am getting an error saying "org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available"
I have also attempted using #MockBean instead of #Autowired to bind the provinceDAO. Even though this prevents the error, it does not persist the entity in the database.
How should I write my testcase to test my method here?
Update:
application-test.properties
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
Entity -> Province.java
#Entity
#Table(name = "w4a_province")
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
#Column(name = "province_name")
private String name;
#Column(name = "province_name_si")
private String nameSi;
#Column(name = "province_name_ta")
private String nameTa;
.
.
}
GenericDAO.java
#Repository
public class GenericDAO<T> implements IGenericDAO<T> {
#PersistenceContext
private EntityManager em;
#Override
public Session getCurrentSession() {
return this.em.unwrap(Session.class);
}
#Override
public T findByPrimaryKey(Class<T> clazz, Object primaryKey) {
return getCurrentSession().find(clazz, primaryKey);
}
#Override
public List<T> findAll(Class<T> clazz) {
DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
return criteria.getExecutableCriteria(getCurrentSession()).list();
}
#Override
public T createEntity(T entity) {
getCurrentSession().save(entity);
return entity;
}
ProvinceDAOImpl.java
#Repository
public class ProvinceDAOImpl extends GenericDAO<Province> implements ProvinceDAO {
}
MetaController.java
#RestController
#PreAuthorize("permitAll()")
public class MetaController {
private final MetaService metaService;
#Autowired
public MetaController(MetaService metService) {
this.metaService = metService;
}
#GetMapping("/meta/provinces")
public ResponseEntity<List<ProvinceDTO>> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Retrieving list of provinces.");
}
List<ProvinceDTO> provinces = metaService.getProvinces();
return ResponseEntity.ok(provinces);
}
}
MetaServiceImpl.java
#Service
#Transactional
public class MetaServiceImpl implements MetaService {
private final ProvinceDAO provinceDAO;
#Autowired
public MetaServiceImpl(ProvinceDAO provnceDAO) {
this.provinceDAO = provnceDAO;
}
public List<ProvinceDTO> getProvinces() {
if (logger.isDebugEnabled()) {
logger.debug("Obtaining a list of provinces from database.");
}
List<Province> entities = provinceDAO.findAll(Province.class);
if (logger.isDebugEnabled()) {
logger.debug("Converting province entities to dtos.");
}
List<ProvinceDTO> dtos = new ArrayList<>();
for (int i = 0; i < entities.size(); i++) {
Province entity = entities.get(i);
if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.SINHALA_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameSi()));
} else if (LocaleContextHolder.getLocale().getLanguage().equals(
GlobalConstants.LanguageIdentifiers.TAMIL_LANGUAGE_TAG)) {
dtos.add(new ProvinceDTO(entity.getId(), entity.getNameTa()));
} else {
dtos.add(new ProvinceDTO(entity.getId(), entity.getName()));
}
}
return dtos;
}
}

I managed to feed the database with the required data by placing a SQL script data-h2.sql with insert queries at the test/resources folder. This prevented the requirement to use an EntityManager or a DAO.
Furthermore, I added the following property to the application-test.properties file.
spring.datasource.platform=h2

In Order to test Rest Api You can try functional test as well as integration test.
You can prepare your own response formate as required and check whether the same is returned or else you can also verify whether the data from db is fine or not.Plz check the below example
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = FactsMain.class)
#WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
In First test case i am forming the response format and trying to return the same and then validating the same.Here i don't need the db data so i have kept service as mock instead of auto wired.And used ObjectMapper for converting json to java and then java obj to json from base Test class.
public class PersonalDetailsControllerTest extends BaseTest {
#MockBean
private IPersonalService service;
private static final String URI = "/api/personalDetails";
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void testGet() throws Exception {
PersonalDetailsEntity entity = new PersonalDetailsEntity();
List<PersonalDetailsEntity> dataList = new ArrayList<PersonalDetailsEntity>();
FactsAdminResponse<PersonalDetailsEntity> dataResponse = new FactsAdminResponse<PersonalDetailsEntity>();
entity.setId(1);
entity.setName(“Anthony Holmes”);
entity.setAge(26);
entity.setCity(“Banglore”);
entity.setCountry(“India”);
dataList.add(entity);
dataResponse.setData(dataList);
Mockito.when(service.getBuildings()).thenReturn(dataList);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(URI)
.accept(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
String expectedJson = this.mapToJson(dataResponse);
String outputInJson = mvcResult.getResponse().getContentAsString();
assertEquals(HttpStatus.OK.value(), response.getStatus());
assertEquals(expectedJson, outputInJson);
}
}
In below case we are getting the actual data in json format as we are doing rest api call and then just validating the status apart from status you can also cross check the data
public class PersonalDetailsControllerTest extends BaseTest {
private static final String URI = "/api/personalDetails";
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void getGet() throws Exception {
MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get(URL)
.accept(MediaType.APPLICATION_JSON_VALUE)).andReturn();
int status = mvcResult.getResponse().getStatus();
assertEquals(200, status);
String content = mvcResult.getResponse().getContentAsString();
//you got the content in string format now you can also validate the data
}

Related

Test a method that returns a ResponseEntity from a service class - Spring boot

I have a service method which returns a ResponseEntity<>, when I am testing the rest endpoint it always returns below error.The error says that it haven't return the expected json.
java.lang.AssertionError: No value at JSON path "$.status"
This is the method I'am using
#Test
void Should_ReturnOk_When_VerifyOtpIsSuccessful() throws Exception {
ResponseWrapper responseWrapper =
new ResponseWrapper(ResponseStatusType.SUCCESS, "SUCCESSFULLY_VERIFIED", null);
ResponseEntity<ResponseWrapper> responseWrapperResponseEntity =new ResponseEntity<>(responseWrapper,HttpStatus.ACCEPTED);
when(otpService.verifySmsOtp(otpVerifyRequestDto)).thenReturn(responseWrapperResponseEntity);
this.mockMvc.perform(MockMvcRequestBuilders
.post(OTP_VERIFY_URI)
.contentType(MediaType.APPLICATION_JSON)
.header("app-key", "xxxx")
.content(asJsonString(otpVerifyRequestDto)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status", is("S")))
;
}
Here ResponseWrapper is a model class
#Getter
public class ResponseWrapper {
private ResponseStatusType status;
private String message;
private String data;
public ResponseWrapper(ResponseStatusType statusType, String message, String data) {
this.status = statusType;
this.message = message;
this.data = data;
}
}
Could you help me out to sort this issue.
Edited:
Seems like the ResponseEntity does not return the JSON itself, it have other objects
Appreciate your input on this.
Test Class Initialization
#AutoConfigureMockMvc
public class OtpControllerTest {
#Mock
private OtpServiceImpl otpService;
private Validator validator = new Validator();
private OtpRequestDto otpRequestDtoEmail = otpRequestDto();
private OtpVerifyRequestDto otpVerifyRequestDto = otpVerifyRequestDto();
#InjectMocks
private OtpController otpController;
private MockMvc mockMvc;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
otpController = new OtpController(otpService, validator);
this.mockMvc = MockMvcBuilders.standaloneSetup(otpController).build();
}
// test methods here
//
}
Thank you.

Unable to mock a method with Mockito and Spring

I have a class JobDelegate for which i am writing unit tests using mockito. I am unable to mock the HTTPOperations class. I have tried using setter injection from test class as well. But it does not work. Below the latest revision of the code. I tried using Power mock. but none of them was helpful. I am unable to predict which is going wrong.
Unit Test code
#ContextConfiguration(locations= "file:src/main/webapp/WEB-INF/spring-
context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
//#RunWith(PowerMockRunner.class)
/#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*" })
//#PrepareForTest({ HTTPOperations.class})
public class JobSubmissionDelegateTest{
private static Logger LOGGER = null;
private JobDelegate jobDelegate ;
private JobManager jobImpl;
#InjectMocks
private HTTPOperations operations;
//#Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
#Before
public void setupTests() {
jobDelegate = new JobDelegate();
jobManager = new DBJobManagerImpl();
operations = new HTTPOperations();
jobManager.setHttpOperations(operations);
jobSubmissionDelegate.setJobImpl(jobManager);
//HTTPOperations httpOperationsSpy =spy(HTTPOperations.class);
//doReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}").when(myClassSpy).method1();
MockitoAnnotations.initMocks(this);
}
#Test
public void testExecuteJob() throws IOException {
// PowerMockito.mockStatic(HTTPOperations.class);
Mockito.when(operations.submitHttpPostRequest(any(), anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
//System.out.println("==>"+operations.submitHttpPostRequest(null, ""));
...........
int runID = jobDelegate.executeJob(jobDetails);
System.out.println("Run ID here " + runID);
}
}
public class JobDelegate {
// This is an interface.. and the implementation is passed from spring-
context.xml
#Autowired
private JobManager jobImpl;
public int executeJob(JobDTO jobDto) {
............
return jobImpl.runBatchJob(jobDto);
}
}
public class DBJobManagerImpl implements JobManager{
#Autowired
private URIUtils uriUtils;
#Autowired
private HTTPOperations httpOperations;
#Override
public int runBatchJob(JobDTO jobDto) throws Exception {
UriComponentsBuilder uri = uriUtils.createURI(ConfigUtil.getUrI());
String response = httpOperations.submitHttpPostRequest(uri, runSubmitJson);
System.out.println("Response ==> " +response);
.................
}
}
I was able to resolve the issue using PowerMock.
Below the code
#RunWith(PowerMockRunner.class)
#ContextConfiguration(locations= "file:src/main/webapp/WEB-
INF/Enrichment_Context.xml")
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PowerMockIgnore({ "javax.xml.*", "org.xml.*", "org.w3c.*",
"javax.management.*" })
#PrepareForTest({ HTTPOperations.class})
public class JobDelegateTest {
#Autowired
private JobDelegate jobSubmissionDelegate;
#Test
public void testExecuteJob() throws IOException {
PowerMockito.mockStatic(HTTPOperations.class);
PowerMockito.when(HTTPOperations.submitHttpPostRequest(Mockito.any(),
Mockito.anyString())).thenReturn("{\"response\":\"{\\\"run_id\\\":32423423}\\n\"}");
...................
int runID = jobSubmissionDelegate.executeJobSubmission(jobDetails);
}
}

Issue with Spring boot Controller with Mockito test case

I am completely new to Mockito and I have to write a test case for my REST Controller, but I am not sure where should I start. Any help would be really appreciated.I've updated my controller based on given suggestion.
Here's my controller:
#RestController
#RequestMapping("/api")
public class TestController {
#Autowired
TestService _testService;
#RequestMapping(value = "/getsearchDetailCourse", method = RequestMethod.GET)
public List<TestDto> getsearchDetailCourse(#RequestParam("courseName") String courseName,
#RequestParam("courseId") Long courseId) throws Exception {
return (List<TestDto>) _testService.searchDetailCourse(courseName, courseId);
}
}
My TestDto:
public class TestDto {
private String numberOfCourse;
private Long courseId;
public TestDto(){}
public TestDto(String numberOfCourse,Long courseId ){
super();
this.numberOfCourse = numberOfCourse;
this.courseId = courseId;
}
public String getNumberOfCourse() {
return numberOfCourse;
}
public void setNumberOfCourse(String numberOfCourse) {
this.numberOfCourse = numberOfCourse;
}
public Long getCourseId() {
return courseId;
}
public void setCourseId(Long courseId) {
this.courseId = courseId;
}
}
Here's my test:
#RunWith(SpringRunner.class)
#WebMvcTest(value = TestController.class, secure = false)
public class TestMethod {
#Autowired
private MockMvc mockMvc;
#MockBean
private TestService testService;
TestDto testDto = new testDto("Test",2744L);
#Test
public void retrieveDetailsForCourse() throws Exception {
Mockito.when(
testService.searchDetailCourse(Mockito.anyString(),
,Mockito.anyLong())).thenReturn(testDto);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/api/getsearchDetailCourse").accept(
MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "[{\"numberOfCourse\":\"Testing1\",\"courseId\":2744},{\"numberOfCourse\":\"Testing2\",\"courseId\":2744}]";
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
I want to test the controller, please help me correct the test case above.

Jersey 2.13 + Bean Validation

I'm using gradle and the following libs:
ext.library['jersey'] = "org.glassfish.jersey:project:2.13"
ext.library['jersey_jettison'] = "org.glassfish.jersey.media:jersey-media-json-jettison:2.13"
ext.library['jersey_jackson'] = "org.glassfish.jersey.media:jersey-media-json-jackson1:2.13"
ext.library['jersey_spring'] = "org.glassfish.jersey.ext:jersey-spring3:2.13"
ext.library['jersey_bean_validation'] = "org.glassfish.jersey.ext:jersey-bean-validation:2.13"
I created the bean validation structure, but its not validating at all. No error messages, nothing. This is the structure I've created:
The DTO
public class MergeSchedulesDto {
#NotNull(message = "validation.invalid.mergeFrom")
private Long mergeFrom;
#NotNull(message = "validation.invalid.mergeTo")
#NotEmpty(message = "validation.invalid.mergeTo")
private List<Long> mergeTo;
The Service
#Path("merge")
#POST
#Consumes({ MediaType.APPLICATION_JSON })
public Response merge(#Valid MergeSchedulesDto dto, #QueryParam("units") List<Long> units) {
The config
public class ApplicationJAXRS extends Application {
public ApplicationJAXRS() {
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
properties
.put("jersey.config.server.provider.packages",
"com.sifionsolution.sig.academic.resource.service,com.sifionsolution.sig.integration.resource.filter,com.sifionsolution.sig.academic.param.converter,com.sifionsolution.sig.datatables.resource.service,com.sifionsolution.sig.datatables.converter");
return properties;
}
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(new Jackson1Feature());
singletons.add(new ValidationExceptionMapper());
return singletons;
}
}
EDIT I forgot the provider:
#Provider
public class ValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
return Response.status(BAD_REQUEST).entity(exception.getMessage()).build();
}
}
EDIT 2: I removed the JUnit test because I didnt test using Jersey Test Framework.
The problem here is that the ValidationExceptionMapper is not beeing called.
Put "#Valid" in your, like this:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response insert(#Valid T obj) throws Exception{
...
}
This works here.

Spring MongoRepository is Null

I have the following code which attempts to save a POJO object (Actor) into MongoDB using Spring Mongo Repository, but the repository object is always Null. I have followed multiple examples but mainly this one
The POJO class:
#Document(collection = "actors")
public class Actor
{
#Id
private String id;
...
//constructor
//setters & getters
}
The repository:
public interface ActorRepository extends MongoRepository<Actor, String>
{
public Actor findByFNameAndLName(String fName, String lName);
public Actor findByFName (String fName);
public Actor findByLName(String lName);
}
The service that uses the repository:
#Service
public class ActorService
{
#Autowired
private ActorRepository actorRepository;
public Actor insert(Actor a)
{
a.setId(null);
return actorRepository.save(a);
}
}
And I access the service from a REST controller class:
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
private ActorService actorService = new ActorService();
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}
The error that I get is NullPointerException from this line: return actorRepository.save(a); in the ActorService.insert() method.
Any Idea why is this happening?
EDIT: Here is the Spring Configurations
#Configuration
public class SpringMongoConfig extends AbstractMongoConfiguration
{
#Bean
public GridFsTemplate gridFsTemplate() throws Exception
{
return new GridFsTemplate(mongoDbFactory(), mappingMongoConverter());
}
#Override
protected String getDatabaseName()
{
return "SEaaS";
}
#Override
#Bean
public Mongo mongo() throws Exception
{
return new MongoClient("localhost" , 27017 );
}
public #Bean MongoTemplate mongoTemplate() throws Exception
{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
The problem is that you are not using Spring to get the ActorService dependency -instead you have manually instantiated the dependency using
private ActorService actorService = new ActorService();.
The following code is the easiest fix in order to inject the ActorService dependency into the controller.
#RestController
public class Controllers
{
private static final Logger logger = Logger.getLogger(Controllers.class);
private static final ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringMongoConfig.class);
#Autowired
private ActorService actorService;
#RequestMapping(value="/createActor", method=RequestMethod.POST)
public #ResponseBody String createActor(#RequestParam(value = "fName") String fName,
#RequestParam(value = "lName") String lName,
#RequestParam(value = "role") String role)
{
return actorService.insert(new Actor(null,fName,lName,role)).toString();
}
...
}

Resources