Skip to content

Spring Boot Unit Test mock third API with MockRestServiceServer #50

Open
@Shellbye

Description

@Shellbye

在尝到过自动化测试的甜头之后,我基本所有的项目都会有配套的自动化测试,之所这里没有说测试驱动开发(Test Driven Development),因为我目前确实还没做到TDD,我目前还是先Develop,然后才写Test,当然我还是希望以后真的能切实做到TDD,因为我一直相信前人经过很多弯路之后留下了的一些经验。

之前我的项目没太多的对第三方服务的依赖,所以写测试的时候,也没有太操心和第三方接口的一些问题,但是这次一个新项目对第三方服务严重依赖,因为基本所有的内容都来自三方服务,但是因为第三方服务也在开发中,就导致他们的接口很不稳定,这也让我的测试用例时通时不通,于是经过一些调研,找到了一些合适的Spring Boot下面基于MockRestServiceServer的模拟第三方服务接口的方法。本项目所有代码都在这里

业务代码

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class DemoApplication {

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


    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

}
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


@RestController
public class MainController {

    private RestTemplate restTemplate;

    @Autowired
    public MainController(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public @ResponseBody
    String demo() {
        String result = restTemplate.getForObject("http://some_third_api_service.com", String.class);
        return String.format("{\"result\": %s}", result);
    }
}

上面基本上就是这个demo项目的所有业务代码了,这里之所以把DemoApplication.class也贴出来,是为了强调这里的RestTemplate这个Bean,因为如果想要用后面的MockRestServiceServer,这里就需要有一个这样的Bean,用于在测试的代码里替换它,如果Controller里面的restTemplate是在demo()方法里new出来的,就无法实现模拟了,读者可以自己尝试一下。

测试代码

package com.example.demo;

import org.junit.Before;
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.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.support.RestGatewaySupport;

import static org.springframework.test.web.client.ExpectedCount.once;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class MainControllerTest {
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    private MockMvc mvc;
    private MockRestServiceServer mockServer;

    @Before
    public void setUp() {
        RestGatewaySupport gateway = new RestGatewaySupport();
        gateway.setRestTemplate(restTemplate);
        mockServer = MockRestServiceServer.createServer(gateway);
    }

    @Test
    public void demoTest() throws Exception {
        mockServer.expect(once(), requestTo("http://some_third_api_service.com"))
                .andRespond(withSuccess("ok123", MediaType.APPLICATION_JSON));

        mvc.perform(MockMvcRequestBuilders
                .get("/")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json("{\"result\":\"ok123\"}"));
        mockServer.verify();
    }
}

从这里的setUp()就可以明白为什么上面的业务代码要用Bean了,否则这里的reset也就没有了意义。这也设置之后,我们就可以在withSuccess写任何我们期望的来自第三方的返回,这也不仅仅可以脱离测试时对第三方的依赖,也可以加快测试的执行速度。

参考资料:

  1. https://stackoverflow.com/questions/25564533/how-to-mock-remote-rest-api-in-unit-test-with-spring
  2. https://examples.javacodegeeks.com/enterprise-java/spring/using-mockrestserviceserver-test-rest-client/

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions