Search code examples
javaspringspring-bootspring-mvcspring-mvc-test

How to create a general MockMvc class for multiple test classes?


I am writing end to end tests for many different Spring Controllers. Right now, I am trying to write a general class for testing that contains MockMvc performing methods. I have endpoints that I need to call in different Controllers and I do not want to copy paste code around, and have a MockMvc and ObjectMapper in each test class.

Few examples of the methods:

public void saveMockDish(DishDto dishDto) throws Exception {
    mockMvc.perform(
        MockMvcRequestBuilders.post(DISH_ENDPOINT)
          .contentType(MediaType.APPLICATION_JSON)
          .content(objectMapper.writeValueAsString(dishDto)))
      .andExpect(status().isCreated());
  }


public DishDto getMockDish(Long id) throws Exception {
    MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders
        .get(DISH_ENDPOINT + "/{id}", id))
      .andExpect(status().isOk())
      .andReturn();
    String contentAsString = mvcResult.getResponse().getContentAsString();
    return objectMapper.readValue(contentAsString, new TypeReference<>() {
    });
  }

What I am trying to accomplish (a bean that I could autowire in another class, example in DishControllerTest class):

@AutoConfigureMockMvc
@TestComponent
public class AppMockMcv {
  private static final String DISH_ENDPOINT = "/dishes";
  private static final String BASE_INGREDIENTS_ENDPOINT = "/base-ingredients";

  @Autowired
  private MockMvc mockMvc;
  @Autowired
  private ObjectMapper objectMapper;

  public List<DishDto> getMockDishes() throws Exception {
  ...

How I want to instantiate my test classes:

@SpringBootTest
public class DishControllerTest {

  @Autowired
  private AppMockMcv appMockMcv;

  @Test
  void testGetDishes() throws Exception {
    List<DishDto> dishes = appMockMcv.getMockDishes();
    assertEquals(4, dishes.size());
    assertNotNull(dishes);
    DishAssertions.containsDish(dishes, "Pasta Carbonara");
  }

Right now I am facing the issue that I can not use @AutoConfigureMockMvc together with @TestComponent, the bean is not found in autowiring. I also tried @Component, @TestConfiguration, @SpringBootTest annotations in the AppMockMcv class.

Current error, although not very useful:

No qualifying bean of type 'mtv.restaurant.mock.AppMockMcv' available: 
expected at least 1bean which qualifies as autowire candidate. 
Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)

I did not find much information on how to create a bean for testing only, also on how to combine it with AutoConfigureMockMvc. Also, I tried to find a way on how to extend MockMvc without success.

What is the right way to achieve what I am trying to accomplish?

  • Spring boot 2.5.4

Solution

  • I did not find much information on how to create a bean for testing only, also on how to combine it with AutoConfigureMockMvc. Also, I tried to find a way on how to extend MockMvc without success.

    If you create bean under /test/java, then it will only be used in tests(in most cases), even if the component annotation is used. Check the information here - https://docs.spring.io/spring-boot/docs/1.4.3.RELEASE/reference/html/boot-features-testing.html (40.3.2).

    Also, I wrote little code for example, it works (Because you didn't provide configuration for Spring, I used only @SpringBootApplication):

    package src:

    @SpringBootApplication
    public class Main {
        public static void main(String[] args) {
            SpringApplication.run(Main.class, args);
        }
    }
    

    package src.test:

    @Component
    public class CustomMockMVc {
        public MockMvc getMockMvc() {
            return mockMvc;
        }
    
        private final MockMvc mockMvc;
    
        public CustomMockMVc(MockMvc mockMvc) {
            this.mockMvc = mockMvc;
        }
    }
    
    @SpringBootTest
    @AutoConfigureMockMvc
    public class TestComponent {
        @Autowired
        private CustomMockMVc customMockMVc;
    
        @Test
        public void testMockMvc() {
            System.out.println(customMockMVc.getMockMvc());
        }
    }