SpringBoot Test Cases not working in the way they were expected to - rest

I have been working on a basic Spring Boot Application building REST APIs. I have learnt to write the APIs and now I am trying to write Unit Tests for them. I have written one unit test for the get API and one for the post API. The post API test seems to be running fine but the get api test fails. I am not sure why. I am not sure if the get test is running before the post and hence nothing is available so it fails?
I have tried changing the order in which the tests are written in order to see the execution order changes but it hasn't changed.
#RunWith(SpringRunner.class)
#WebMvcTest(value = ProjectRestController.class)
public class ProjectControllerTest
{
private String baseURL = "http://localhost:8080/";
private String expectedResult = "{id:0, name:\"Testing Course 0\", description: \"This is a test for course 0\"}";
#Autowired
private MockMvc mockMvc;
#MockBean
private ProjectService projectService;
Project mockProject = new Project(0, "Testing Course 0", "This is a test for course 0");
#Test
public void addProject() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.post(baseURL+"/projects")
.content(asJsonString(new Project(0, "Test 0", "Testing Project 0")))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
//This test does not seem to work. Returns 404
#Test
public void getProject() throws Exception
{
mockMvc.perform(MockMvcRequestBuilders.get(baseURL+"/projects/0")
.accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
}
public static String asJsonString(final Object obj)
{
try
{
return new ObjectMapper().writeValueAsString(obj);
} catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
I expected a status 200 from the GET as the post is working fine however the get returns a 404.

The issue is due to you have a base URL you should not send the base URL, pass only /project/0

Related

Why is my data not persisted/accessible in an Spring-Boot integration-test with HTTPGraphQLTester and TestEntityManager

I have a bare-bones Spring-Boot app with some GraphQL endpoints and a Postgres database and want to run an integration test against an endpoint. It should find an entity by its ID and does so without a problem when I send a request manually via Postman. However when I write an integration test for the controller it doesn't. The data seems to be saved after using
TestEntityManager (or the JpaRepository directly) an I get the entity back with its ID. I then stick that ID into a query with HttpGraphQlTester which fails with an empty result/null. I traced it with the debugger and discovered that when the endpoint calls the repository to retrieve the entity with the given ID it gets null or when I look at all the repo-contents it's just an empty list. So my data seems to be accessible in my test but not in my repo/service. Any pointers would be very much appreciated.
Test
#SpringBootTest
#AutoConfigureHttpGraphQlTester
#AutoConfigureTestEntityManager
#Transactional
public class BackboneTreeControllerTest {
#Autowired
HttpGraphQlTester tester;
#Autowired
private TestEntityManager testEntityManager;
#Test
void findTaxon() {
Taxon taxon = Taxon.builder()
.path(Arrays.asList("path", "to", "taxon"))
.nameCanonical("Cocos nucifera")
.authorship("Me")
.extinct(false)
.numDescendants(1l)
.numOccurrences(1l)
.build();
Taxon savedTaxon = testEntityManager.persistFlushFind(taxon); // (1)
this.tester.documentName("queries")
.operationName("FindTaxon")
.variable("taxonId", savedTaxon.getId())
.execute()
.path("findTaxon.authorship")
.entity(String.class)
.isEqualTo("Me");
the testEntityManager returns successfully with an ID.
Query
query FindTaxon($taxonId: ID!) {
findTaxon(id: $taxonId) {
authorship
}
}
Controller
#Controller
#AllArgsConstructor
public class BackboneTreeController {
private final TaxonService taxonService;
#QueryMapping
public Taxon findTaxon(#Argument Integer id) {
Optional<Taxon> taxon = taxonService.findTaxon(id);
return taxon.orElse(null);
}
}
Service
#Service
#AllArgsConstructor
public class TaxonService {
private final TaxonRepository taxonRepository;
public Optional<Taxon> findTaxon(Integer id) {
return taxonRepository.findById(id); // (2)
}
}
This is where I would expect the repo to return the entity but it does not. Also using .findAll here returns an empty list.
Repository
#Repository
public interface TaxonRepository extends JpaRepository<Taxon, Integer> {
}
Note that everything works fine when I just run the app and send the exact same query manually!
I don't know HttpGraphQlTester but I'd assume that it generates requests which then get processed in a separate thread.
That thread won't see the changes made in the test because they aren't committed yet.
If this is the reason resolve it by putting the setup in it's own transaction, for example by using TransactionTemplate.

SpringbootTest + TestContainers: how do I refresh the database after tests pollute the database

I am using an abstract class like this:
#SpringBootTest(classes = MyAppApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public abstract class AbstractIntegrationTest {
static {
PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer().withPassword("password")
.withUsername("postgres").withDatabaseName("MyApp");
postgreSQLContainer.start();
System.setProperty("spring.datasource.url", postgreSQLContainer.getJdbcUrl());
System.setProperty("spring.datasource.password", postgreSQLContainer.getPassword());
System.setProperty("spring.datasource.username", postgreSQLContainer.getUsername());
}
Then I have many tests that leverage that use that class like this:
public class moreTests extends AbstractIntegrationTest {
TestRestTemplate restTemplate = new TestRestTemplate("my-user", "password");
HttpHeaders headers = new HttpHeaders();
#Test
public void SimpleHealthCheck() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/api/v1/healthcheck"),
HttpMethod.GET, entity, String.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
#Test
public void GetInst() {
HttpEntity<String> entity = new HttpEntity<String>(null, headers);
ResponseEntity<String> response = restTemplate.exchange(
createURLWithPort("/api/v1/institutions"),
HttpMethod.GET, entity, String.class);
assertThat(response.getStatusCode(), equalTo(HttpStatus.OK));
}
However, some of my tests will pollute the database. I'd like to control if a test runs with a fresh database or not. What's the prescribed way to do this?
After more reading about Spring boot integration testing, it appears the prescribed way is to use the "#DirtiesContext" annotation for tests that are destructive (or dirty).
EDIT: After a few months, I realized #DirtiesContext is not awesome. It basically resets the whole app which can be expensive. Also, #DirtiesContext May not reset your database in some cases depending on how your app works. I suggest having a cleanup SQL script that runs in your #BeforeAll or #AfterAll section of each test class. This cleanup SQL script needs to be carefully written.
you either use the #Before annotation to clean everything before executing your tests.
Or you clean in each test before you execute.
Each test should be independent from the other. So usually:
clear and set up expectations
run test
If test fails, your database will be in the failed state so you can check what happened.

Spring boot restful service Post request always returns null as response message in test code using Junit

This is a demo controller.
#PostMapping("/rest/new")
public ResponseEntity<MessageDto> newUser(#RequestBody UserDto userDto) {
return ResponseEntity.ok(new MessageDto().setMessage(userService.createUser(userDto)));
}
This is service layer.
#Override
public String createUser(UserDto userDto) {
// Do Something
return "Successful!!";
}
This is the test code to test the controller
#Test
public void testPostRestController() throws Exception {
UserDto userDto = new UserDto();
userDto.setName("AA");
userDto.setEmail("a#a.a");
userDto.setId((long) 1);
when(userService.createUser(userDto)).thenReturn("Successful!!");
mockMvc.perform(post("/rest/new")
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
.content(new ObjectMapper().writeValueAsString(userDto)))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.message", is(userService.createUser(userDto))))
.andDo(MockMvcResultHandlers.print());
}
The problem is when I run the test code, it is supposed to check the response status and response message. The response status matched but the problem is the response message always returns as null.
error:
java.lang.AssertionError: JSON path "$.message"
Expected: is "Successful!!"
but: was null
Am I missing something here?
Try using
when(userService.createUser(any(UserDto.class))).thenReturn("Successful!!");
Instead of
when(userService.createUser(userDto)).thenReturn("Successful!!");
This should ideally fix the NPE

No #ResponseBody returned from #ExceptionHandler in Spring boot app deployed in Tomcat

I have a Spring Boot web app that runs just fine from STS but shows different behavior when running in Tomcat from a WAR file.
I use Thymeleaf to handle all my web pages but I have a couple pages that are using jQuery to send async calls and make user experience more dynamic.
Anyway, I have a Controller method that calls a service method which may throw a RuntimeException which I handle this way :
#ExceptionHandler(MyRuntimeException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public #ResponseBody String handleMyRuntimeException(MyRuntimeException exception) {
return "Oops an error happened : " + exception.getMessage();
}
In JS, I use the response body I return above to display a message on screen.
That works perfectly fine when running my app in STS but once I switch to deploy it in Tomcat the ErrorPageFilter gets invoked and in doFilter() it does:
if (status >= 400) {
handleErrorStatus(request, response, status, wrapped.getMessage());
response.flushBuffer();
}
In handleErrorStatus() it creates an error with the status and associated message but doesn't return my response.
I haven't figured out how to solve this and I'd really appreciate if anybody could help.
Thanks!
I went around this issue (I would think that is a Spring Boot issue) by doing the following.
Separate Rest and Mvc controllers
See my question here : Spring MVC: Get i18n message for reason in #RequestStatus on a #ExceptionHandler
Inject Jackson converter and write response myself :
#ControllerAdvice(annotations = RestController.class)
#Priority(1)
#ResponseBody
public class RestControllerAdvice {
#Autowired
private MappingJackson2HttpMessageConverter jacksonMessageConverter;
#ExceptionHandler(RuntimeException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public void handleRuntimeException(HttpServletRequest request, HttpServletResponse response, RuntimeException exception) {
try {
jacksonMessageConverter.write(new MyRestResult(translateMessage(exception)), MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
response.flushBuffer(); // Flush to commit the response
} catch (IOException e) {
e.printStackTrace();
}
}
}

Unit testing "object reference not set to an instance " at NUnit

i have a ASP.Net project and Nunitasp framework work for unit testing,i have a object in account.aspx.cs file when i tried to test the object(NugetplatformModel) value i get"object reference not set to an instance" error,
my account page code is given below
public partial class Account : System.Web.UI.Page
{
public NugetPlatformModel NugetPlatformModels;
public string result = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
if (!WebSecurity.IsAuthenticated)
{
Response.Redirect("/login", true);
}
else
{
result = "success";
NugetPlatformModels = new NugetPlatformModel();
}
}
my test case code is given below
[Test]
public void AccountPage_ValidCredential_AccessModel()
{
Browser.GetPage(domain + "account");
string ExpectedPage = domain + "account";
logon();
Account acccountPage = new Account();
AssertEquals("success", acccountPage.result);
AssertEquals("should have license",true,acccountPage.NugetPlatformModels.IsHavingLicense);
}
How can I access and test that code behind variables? when start the testing the NUgetplatformmodel has been assigned i have checked it by debugging but after that in nunit gui it displays null reference error, i thought there is a problem in accessing variable in testcase..please help me..
It seems your code is not complete. From what I see here your account needs to run Page_Load in order to fill result and NugetPlatformModels. But I do not see how this method is launched in your test. Is it run from the constructor of Account?
It would be helpfull if you put all the code for Account in your post.