Make my #Test GET request work in Spring Boot - java

beginner here. I have created an API in Spring Boot using java and I want to test whether the GET request returns a 200. I'm not interested in checking what's returned (a JSON object) I just want to check whether this connection works. I have tried this code so far:
#Test
void getRequest() throws Exception {
// this doesn't work because a ResultMatcher is needed
this.mvc.perform(get("/currencies")).andExpect(HttpStatus.ACCEPTED);
//tried this too and I get "Cannot resolve method 'assertThat(int, int)'"
RequestBuilder request = get("/currencies");
MvcResult result = mvc.perform(request).andReturn();
assertThat(result.getResponse().getStatus(), 200);
}
On the first statement, I'm literally saying "do a get request for this base url and expect a http accepted status" it isn't liking it. Am I asking for too much?
my second attempt I'm saying "Make a MvcResult object and store the result of the GET request within it. Then compare it to the status code of 200"
Here is the full class
import com.example.CoinAPI.controller.CoinController;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
#AutoConfigureMockMvc
#SpringBootTest
class CoinApiApplicationTests {
#Autowired
private CoinController controller;
#Autowired
private MockMvc mvc;
#Test
void contextLoads() {
assertThat(controller).isNotNull();
}
#Test
void getRequest() throws Exception {
// this doesn't work
this.mvc.perform(get("/currencies")).andExpect(HttpStatus.ACCEPTED);
//tried this too
RequestBuilder request = get("/currencies");
MvcResult result = mvc.perform(request).andReturn();
assertThat(result.getResponse().getStatus(), 200);
}
}
How do I make this work? I have searched endlessly around Google and youtube. nothing is helping me. I am missing something, I am sure

Related

Java Unit Testing Http Get test user auto generate

I write function in rest to automatically generate user with admin role. Here's the function:
UserController.java
#RestController
#RequestMapping("users")
public class UserController {
#Autowired
private UserRepository userRepo;
#Autowired
private TokenRepository tokenRepo;
#GetMapping("admin")
public String getAdmin () {
JSONObject report = new JSONObject();
String dataAdmin = userRepo.findByUsername("admin");
if(dataAdmin == null) {
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
userRepo.save(myadmin);
report.put("message", "admin generated");
} else {
report.put("message", "admin only generated once");
}
return report.toString();
}
I am trying to follow the instruction from here https://www.springboottutorial.com/unit-testing-for-spring-boot-rest-services.
In Unit Testing Http Get Operation section. I am getting several problem and also trying the different solution until I meet this Unit testing a Get request in a spring project from stackoverflow.
below is the testing script I've make so far.
package blablaaa.order;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
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.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import blablaaa.order.controller.UserController;
import blablaaa.order.dao.UserRepository;
import blablaaa.order.model.User;
//#ExtendWith(SpringExtension.class)
//#SpringBootTest
#WebMvcTest(value = UserController.class)
class OrderApplicationTests {
//
#Autowired
private MockMvc mockMvc;
#MockBean
private UserRepository userRepo;
#Test
void contextLoads() throws Exception{
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
List<User> myUser = new ArrayList<>();
myUser.add(myadmin);
RequestBuilder rb = MockMvcRequestBuilders.get("/users/admin").accept(MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(rb).andReturn();
JSONObject expect = new JSONObject();
expect.put("message", "admin generated");
// System.out.println(result.toString());
System.out.println(expect.toString());
// Assertions.assertTrue(result.toString().contains(expect.toString()));
}
}
I dont know, how the testing should be written. any keyword related to this?
[update]
Here's my main:
// OrderApplication.java
#SpringBootApplication
#EnableMongoRepositories("blablaaa.order.dao")
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Here's my program error log
Description:
Field tokenRepo in blablaaa.order.controller.UserController required a bean named 'mongoTemplate' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named 'mongoTemplate' in your configuration.
The first thing you have to do is to clearly understand what your method getAdmin is doing and secondly clearly identify your dependencies and how they interact in the main method.
To finish, you have to define what you want to test, in your case you have two cases. The if case and the else case.
In your test you must define the behavior of your mock with a when().thenReturn()...
Your test method will look like this:
#WebMvcTest(value = UserController.class)
class OrderApplicationTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private UserRepository userRepo;
#Test
void contextLoads_whenNotNull() throws Exception{
User myadmin = new User();
myadmin.setUsername("admin");
myadmin.setFirstname("admin");
myadmin.setLastname("admin");
myadmin.setEmail("admin#admin");
myadmin.setRole("admin");
when(userRepo).thenReturn(myadmin)
mockMvc.perform(get("/users/admin")
.contentType(MediaType.APPLICATION_JSON))
.andExpectAll(
status().isOk(),
jsonPath("$.message", containsString("admin generated"))
);
}
#Test
void contextLoads_whenIsNull() throws Exception{
when(userRepo).thenReturn(null)
mockMvc.perform(get("/users/admin")
.contentType(MediaType.APPLICATION_JSON))
.andExpectAll(
status().isOk(),
jsonPath("$.message", containsString("admin only generated once"))
);
}
}
For more details: https://www.bezkoder.com/spring-boot-webmvctest/
previously i am laravel user. When i run unit testing, i dont need to run the apps before i can run testing scenario. So i think, the error log tell me that the mongo database is not connected to apps, even the apps itself is not running yet.
So, based on my case, i need to make sure run the apps first, not build. If i only build the apps, the program will remain error.
By default, when you need to build the apps, you need to make sure the apps is already running too. Spring Boot will also run the test case before build the jar file.
first, run the apps
mvn spring-boot:run
then,
you can run only the test case
mvn test
or build your apps
mvn clean package

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.

Spring boot WebMvcTest cases keep getting terminated and won't run

When I try using the below code to test the controllers no errors happen, it just says terminated with no logged messages or anything.
import static org.junit.Assert.assertEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
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.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import ....controllers.UserController;
import ....data.response.UserResponse;
import ....models.user.User;
#RunWith(SpringRunner.class)
#WebMvcTest(UserController.class)
public class UserWebMvc {
#Autowired
private MockMvc mvc;
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray() throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(), "hello");
// userService.createUser(
// new UserRequest(alex.getInfo().getName(), alex.getInfo().getEmail(), alex.getInfo().getPassword()));
//
}
}
However my test to check that junit is working runs fine, so I'm thinking its something to do with SpringRunner or WebMvc
#SpringBootTest
class BackendApplicationTests {
#Test
void contextLoads() {
assertTrue(false);
}
}
Isn't assertTrue(false) always going to be false? You are basically checking whether false equals true, which will always fail.
Also, from the Spring documentation regarding #WebMvcTest:
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent, Converter/GenericConverter, Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
Using this annotation, it will not scan any beans from the service layer. So if you have a UserService which is injected into the UserController, it will not be found by Spring.
When I run tests like this, I would do something like:
class UserControllerTest {
#InjectMocks
private UserController userController;
private MockMvc mockMvc;
#BeforeEach
public void setUp() {
MockitoAnnotations.openMocks(this);
mockMvc =
MockMvcBuilders.standaloneSetup(userController).build();
}
#Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
User alex = new User();
List<UserResponse> allUsers = Arrays.asList(new
UserResponse(alex.getId(), alex.getInfo()));
RequestBuilder request = MockMvcRequestBuilders.get("/hello");
MvcResult result = mvc.perform(request).andReturn();
assertEquals(result.getResponse().getContentAsString(),
"hello");
}
}
I was using the wrong import. Changing to this fixed it
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

rolling back database changes, from a remote REST API test case call

Our project is built with spring boot. Within this project, we've built lot of REST API implementations using the java language, with REST APIs being invocable at URI end-points. Many of these REST API implementations, interact with RDBMS database at back-end (we're using postgresql). The response of all our REST APIs is JSON.
I'm writing API tests that test these REST APIs, using java within the test case, and using JUnit library to write these tests.
I'm writing two kinds of these API tests, as follows,
1) For the REST APIs, that only do read from the database, the test case implementation is simple. I use an HTTP client library from java within my test case, and issue a GET or POST request. I then do 'assert' on the returned JSON response within the test case.
2) For the REST APIs, that do one of create, update or delete on the database, I'm facing challenges to roll back from the database create, update or delete changes that my test case does. In fact, at this moment, I don't know how to roll back create, update or delete changes that my test case does, just before my test case exits. Can anyone please give pointers how to solve this?
Below is also mentioned present source code of one of my test case,
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class ApplnCommonRestControllerTest {
private static String API_URI_2 = "http://localhost:8081/appln-restapis/rest/public/common/getalluniversities";
private static CloseableHttpClient fHttpClient;
#Before
public void setUp() throws Exception {
fHttpClient = HttpClients.createDefault();
}
#After
public void tearDown() throws Exception {
fHttpClient.close();
}
#Test
public void getAllUniversitiesResponseBodyTest() throws ClientProtocolException, IOException {
HttpGet httpGet = new HttpGet(API_URI_2);
HttpResponse httpResponse = fHttpClient.execute(httpGet);
InputStream inpStream = (httpResponse.getEntity()).getContent();
String respBodyJsonString = Utilities.inputStreamToString(inpStream);
JSONObject jsonResponeObj = new JSONObject(respBodyJsonString);
JSONArray dataJsonArray = jsonResponeObj.getJSONArray("data");
assertTrue(dataJsonArray.length() >= 2); // test that array contains at least two elements
JSONObject arrElem = (JSONObject)dataJsonArray.get(0); // test first element of array
int univId = arrElem.getInt("id");
String univName = arrElem.getString("universityname");
assertTrue(univId == 1 && univName.length() > 0);
arrElem = (JSONObject)dataJsonArray.get(dataJsonArray.length() - 1);
// test last element of array
univId = arrElem.getInt("id");
univName = arrElem.getString("universityname");
assertTrue(univId > 1 && univName.length() > 0);
}
}
The above sample test case code from our project, does an HTTP GET database read only call. I'm looking for a database roll back solution for the test case, in case the REST API invoked by the test case did a database create/update/delete (i.e, just before or after the test case method exited, the database create/update/delete changes should roll back).
Generally I tend to mark all my #Test methods as #Transnational. But as your test case is completely detached from application context of course this will not help. spring has great integration test library for web layer MockMvc. Using this library your test will look like:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.transaction.annotation.Transactional;
import static org.hamcrest.Matchers.*;
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class ApplnCommonRestControllerTest {
#Autowired
MockMvc mockMvc;
#Test
#Transactional
public void getAllUniversitiesResponseBodyTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/appln-restapis/rest/public/common/getalluniversities"))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.jsonPath("$.data")
.value(iterableWithSize(2)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].id", is(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].universityname", not(emptyArray())))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[1].id", greaterThan(1)))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[1].universityname", not(emptyArray())));
}
}
And also your test will rollback as this is not really external http call and #Transactional do work in this scenario.

Cucumber StepDef ResultActions NullPointerException

I am receiving the following NullPointerException when trying to execute a Cucumber StepDefs file that passes a JSON from a .feature file to a REST endpoint on localhost;
I have tried to instantiate the ResultActions in every other way, receiving the same error.
The Controller works linked to the test is fine, and is pointing to the correct REST endpoint.
The issue is with the result in personStepDefs
I don't think I'm missing a parameter for ResultActions result as I've built my RequestBuilder
java.lang.NullPointerException at com.///.v2.PersonStepDefs.i\_add\_a\_new\_Person\_using\_POST\_at\_with\_JSON([PersonStepDefs.java:49](https://PersonStepDefs.java:49)) at
✽.I add a new Person using POST at "[http://localhost:8080/services/person/add](http://localhost:8080/services/person/add)" with JSON:(file:///C:/path/to/src/test/resources/Person.feature:6)
PersonStepDefs.java
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#Transactional
/**
* Person Step Definition class to execute Scenario contained in Person.feature
* #author Lewis Jones
*
*/
public class PersonStepDefs {
#Autowired
private volatile WebApplicationContext wac;
#Autowired
private volatile MockMvc mockMvc;
private ResultActions result;
/**
* Runs the application server before every scenario.
*/
#BeforeClass
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#When("I add a new Person using POST at {string} with JSON:")
public void i_add_a_new_Person_using_POST_at_with_JSON(String request, String json) throws Exception {
result = mockMvc.perform(post(request).contentType(MediaType.APPLICATION_JSON)
.content(json.getBytes()));
}
#Then("the response code should be {int}")
public void the_response_code_should_be(Integer responseCode) throws Exception {
result.andExpect(status().is(responseCode));
}
}
RunMvcTest.java
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty","html:build/cucumber-html-report"},
features = "src/test/resources", strict = true)
#WebAppConfiguration
#ContextConfiguration(classes = V2Application.class)
/**
* A class to run the Cucumber .feature files located in 'features'
* #author Lewis Jones
*
*/
public class RunMvcTest {
}
Person.feature
Feature: Person CRUD
As a User, I want to add a Person
#repo
Scenario: Person.Repo.Add
When I add a new Person using POST at "http://localhost:8080/services/person/add" with JSON:
"""
{"firstName":"Lewis","lastName":"Jones","addressId":"1", "dob":"1999-07-11"}
"""
Then the response code should be 200
A null pointer exception happens when you try to de-reference a variable or field that is null. So if you try to call perform on mockMvc while mockMvc is null you will get a null pointer exception.
If you read the stack trace carefully you can see that this it what it tries to tell you.
result = mockMvc.perform(....);
So how can mockMcv be null? You initialize it in setup method right? That means setup isn't called. You can confirm this by putting a break point in the method and debugging your test.
import org.junit.BeforeClass;
....
/**
* Runs the application server before every scenario.
*/
#BeforeClass
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
And setup doesn't get called because BeforeClass is a JUnit annotation. Cucumber uses io.cucumber.java.Before.

Categories