How to test this method with spring boot test?

2019-07-28 19:11发布

问题:

I want test the method like this

@PostMapping(value = "/test")
public String test(@Valid TestModel model) {
    return model.getUsername();
}

and the TestModel is this

@Getter
@Setter
public class TestModel {
    private MultipartFile[] image1;
    private MultipartFile[] image2;
    private MultipartFile[] image3;
    private String username;
    private String password;
}

I can use httpclient to test this but I don't think this is a good idea,so any other methods with spring test?

回答1:

If, on the other hand you want to set a fast execution unit test without having to start SpringBoot alltogether... read on.

Using @SpringBootTest sets up a full integration testing environment which starts a full SpringBoot instance so it is relatively time consuming when actively developing. Spring MockMVC comes to the rescue as it emulates a servletContainer to a point where you can incorporate any request filtering or exception handling tests in your unit tests suite.

You can configure this setup with the following approach:

Configure a tailored test context: @ContextConfiguration allows you specify the classes you need for your test. Set Mockito MockMvc which moreless emulates a servlet container and set your tests fixture and dependencies.

 @RunWith(SpringRunner.class)
@ContextConfiguration(classes = {
    WebConfig.class,
    SomeFilter.class,
    HeaderFactory.class,
})
@Slf4j
public class OrganisationCtrlTest {

    private MockMvc mvc;

    private Organisation coorg;

    @Autowired
    private SomeFilter someFilter;

    @MockBean
    private OrganisationSvc service;

    @InjectMocks
    private OrganisationCtrl controller = new OrganisationCtrl();

    //Constructor
    public OrganisationCtrlTest() {
    }
   ....

Configure a mock MVC "servlet emulator": register handler beans in the context and build the mockMvc emulator (Note: there are two possible configuration: standaloneSetup or webAppContextSetup; refer to the documentation). The builder rightfully implements the Builder pattern so you can chain configuration commands for exception resolvers and handlers before calling build().

@Before
public void setUp() {
    final StaticApplicationContext appContext = new StaticApplicationContext();
    appContext.registerBeanDefinition("BusinessExceptionHandler",
            new RootBeanDefinition(BusinessExceptionHandler.class, null, null));
    appContext.registerBeanDefinition("InternalExceptionHandler",
            new RootBeanDefinition(InternalExceptionHandler.class, null,
                    null));
    MockitoAnnotations.initMocks(this);
    mvc = MockMvcBuilders.standaloneSetup(controller)
            .setHandlerExceptionResolvers(getExceptionResolver(appContext))
            .addFilters(someFilter)
            .build();
    coorg = OrganisationFixture.getFixture("orgID", "name", "webSiteUrl");
}
....

Run your tests

    @Test
    public void testGetSingleOrganisationRecordAndSuccess() throws Exception {
        System.out.println("testGetSingleOrganisationRecordAndSuccess");
        String request = "/orgs/{id}";
        log.info("Request URL: " + request);

        when(service.getOrganisation(anyString())).
                thenReturn(coorg);
        this.mvc.perform(get(request)
                .accept(VndMediaType.UNITERRA_RFV1_JSON_UTF8)
                .header("Accept-Language", "en"))
                .andExpect(content().contentType(
                        .APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("org")))
                .andExpect(content().string(containsString("aName")))
                .andExpect(content().string(containsString("aUrl")))
                .andDo(print());
    }
    ....
}

Hope this helps.

Jake.



回答2:

When you test the Controllers, you are probably doing an integration testing. I write Spring MVC based test cases and with Spring boot, more power to @AutoConfigureMockMvc

Reference: https://docs.spring.io/spring/docs/5.0.5.RELEASE/spring-framework-reference/testing.html#spring-mvc-test-framework

http://www.baeldung.com/spring-boot-testing

It goes something like this after configuration:

@RunWith(SpringRunner.class)
@SpringBootTest(
  webEnvironment = WebEnvironment.RANDOM_PORT,
  classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource(
  locations = "classpath:test.properties")
public class ControllerTest {
 
    @Autowired
    private MockMvc mvc;

    @Test
    public void test(){
      mvc.perform(MockMvcRequestBuilders
              .post("/test")
              .contentType(MediaType.APPLICATION_JSON).content(content))
              .andExpect(status().isOk())
              .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
              .andDo(print());

    }


回答3:

It’s really easy with MockMvc. Also you can check your response with JsonPath. Spring MVC offers a standaloneSetup that supports testing relatively simple controllers, without the need of context.

Build a MockMvc by registering one or more @Controller's instances and configuring Spring MVC infrastructure programmatically. This allows full control over the instantiation and initialization of controllers, and their dependencies, similar to plain unit tests while also making it possible to test one controller at a time.

An example test for your controller can be something as simple as

public class DemoApplicationTests {

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = standaloneSetup(new HelloWorld()).build();
    }

    @Test
    public void testSayHelloWorld() throws Exception {
        this.mockMvc.perform(get("/").accept(MediaType.parseMediaType("application/json;charset=UTF-8")))
                .andExpect(status().isOk())
                .andExpect(content().contentType("application/json"));

    }
}


回答4:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {

@Autowired
private WebApplicationContext wac;

private MockMvc mockMvc;

@Before
public void before() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();   //构造MockMvc
}

@Test
public void test() throws Exception {
    this.mockMvc.perform(MockMvcRequestBuilders.multipart("/test")
            .file(new MockMultipartFile("image1", "filename1.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image1", "filename2.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image2", "filename3.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image2", "filename4.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image2", "filename5.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image1", "filename6.txt", "text/plain", "some xml".getBytes()))
            .file(new MockMultipartFile("image3", "filename7.txt", "text/plain", "some xml".getBytes()))
            .param("username", "123")
            .param("password", "123")
    ).andExpect(MockMvcResultMatchers.status().is(200));
}

}