Im working on a Travel Blog project in Spring Boot and Im trying to do TDD. Im in the process of writing the tests for my BlogEntryController class which handles adding, deleting etc of BlogEntries.
BlogEntryControllerTest
package com.example.server;
import com.example.server.controller.BlogEntryController;
import com.example.server.models.user.BlogEntry;
import com.example.server.repositories.BlogEntryRepository;
import com.example.server.services.BlogEntryService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.context.TestConfiguration;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@WebMvcTest(controllers = BlogEntryControllerTest.class)
@ExtendWith(SpringExtension.class)
public class BlogEntryControllerTest {
@TestConfiguration
static class TravelEntryControllerTestConfiguration {
@Autowired
BlogEntryRepository blogEntryRepository;
@Bean
public BlogEntryService blogEntryService() {
return new BlogEntryService(blogEntryRepository);
}
}
private BlogEntryController blogEntryController;
@Autowired
private MockMvc mockMvc;
@MockBean
BlogEntryService blogEntryService;
@MockBean
private BlogEntryRepository blogEntryRepository;
@Autowired
private ObjectMapper objectMapper;
@Test
void getAllBlogEntriesTest() throws Exception {
BlogEntry blogEntry1 = new BlogEntry("Test title 1", "headerPictureSource 1", "This is the main content");
BlogEntry blogEntry2 = new BlogEntry("Test title 2", "headerPictureSource 2 ", "content");
List<BlogEntry> blogEntryList = Arrays.asList(blogEntry1, blogEntry2);
Mockito.when(blogEntryService.getAllBlockEntries()).thenReturn(blogEntryList);
mockMvc.perform(get("/blog/entries")).andExpect(status().is2xxSuccessful());
}
}
BlogEntryController
package com.example.server.controller;
import com.example.server.models.user.BlogEntry;
import com.example.server.services.BlogEntryService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
public class BlogEntryController {
private final BlogEntryService blogEntryService;
public BlogEntryController(BlogEntryService travelEntryService) {
this.blogEntryService = travelEntryService;
}
@GetMapping("/blog/entries")
public List<BlogEntry> getAllBlogEntries() throws Exception {
return blogEntryService.getAllBlockEntries();
}
}
BlogEntryService
package com.example.server.services;
import com.example.server.models.user.BlogEntry;
import com.example.server.repositories.BlogEntryRepository;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogEntryService implements IBlogEntryService {
private final BlogEntryRepository blogEntryRepository;
public BlogEntryService(BlogEntryRepository travelEntryRepository) {
this.blogEntryRepository = travelEntryRepository;
}
public List<BlogEntry> getAllBlockEntries() {
return blogEntryRepository.findAll();
}
}
BlogEntryRepository
package com.example.server.repositories;
import com.example.server.models.user.BlogEntry;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BlogEntryRepository extends JpaRepository<BlogEntry, Long> {
}
BlogEntry
package com.example.server.models.user;
import jakarta.annotation.Nullable;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Objects;
@Entity
@Table(name = "blogEntries")
@NoArgsConstructor
public class BlogEntry {
@Id
@Getter
@Setter
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "title")
@Getter
@Setter
private String title;
@Getter
@Setter
@Column(name = "headerPicture")
private String headerPicture;
@Getter
@Setter
@Column(name = "mainContent")
private String mainContent;
public BlogEntry(String title, String headerPicture, String mainContent) {
this.title = title;
this.headerPicture = headerPicture;
this.mainContent = mainContent;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof BlogEntry))
return false;
BlogEntry blogEntry = (BlogEntry) o;
return Objects.equals(this.id, blogEntry.id) && Objects.equals(this.title, blogEntry.title)
&& Objects.equals(this.headerPicture, blogEntry.headerPicture) && Objects.equals(this.mainContent, blogEntry.mainContent);
}
@Override
public String toString() {
return "BlogEntry{" "id=" this.id ", title='" this.title '\'' ", headerPicture='" this.headerPicture '\'' ", mainContent='" this.mainContent '\'' '}';
}
}
MockHttpServletRequest:
HTTP Method = GET
Request URI = /blog/entries
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Range for response status value 404 expected:<SUCCESSFUL> but was:<CLIENT_ERROR>
Expected :SUCCESSFUL
Actual :CLIENT_ERROR
<Click to see difference>
These are the involved classes.
My error output looks like this
I have already done the same for my User Entity so I figured I can write my Controllers, Services and tests and a similar fashion.
Im kinda out of expertise here. Any ideas?
Thanks a lot in advance
I have already tried to find a solution and checked various posts where HTTP requests methods were swapped (used post instead of get) or that I should use @ExtendWith or Mockmvc but obviously Im already doing that.
Also I noticed that most of those posts refer to problems when testing the POST method, however, here its the GET method.
I checked some suggested annotations on my controller method such as @ResponseBody or tried @RequestMapping with the respective params instead of @GetMapping.
I also checked wether my imports are correct and are the same as the once from my UserControllerTest cause Im basically doing the same stuff and there it all works.
Also note: My requests work in postman. So it seems as if its only a problem with the test itself, not the application.
CodePudding user response:
I think the following is incorrect
@WebMvcTest(controllers = BlogEntryControllerTest.class)
Should be:
@WebMvcTest(controllers = BlogEntryController.class)
There might be other issues after this change.
CodePudding user response:
The HTTP status code 405 (Method Not Allowed) indicates that the requested HTTP method is not supported for the requested resource. To fix this issue in a Spring Boot application with JUnit 5, you can check the following points:
Verify the HTTP method specified in the test: Ensure that you are using the correct HTTP method (e.g. GET, POST, PUT, DELETE, etc.) in the test method's @GetMapping or other appropriate annotation (e.g. @PostMapping).
Verify the URL path: Ensure that the URL path specified in the @GetMapping annotation matches the URL path of the endpoint being tested.
Disable CSRF protection: If your application has enabled CSRF protection, it may prevent you from making GET requests in tests. You can disable CSRF protection by adding the following to your test configuration class:
@TestConfiguration
public class TestConfig {
@Bean
public WebSecurityConfigurerAdapter securityConfigurerAdapter() {
return new WebSecurityConfigurerAdapter() {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
};
}
}