转载

springboot入门06 – 接口单元测试方案

以前写过关于springboot Controller层单元测试的系列文章( Spring Controller层测试 )。但是那几篇文章还是更偏方法论一些,不能直接拿来使用。所以有了这偏内容,目的主要是记录下平时使用的Controller层单元测试方案。

在这里先定义一个普通的api接口类 WorkerApi

@RestController
@RequestMapping(value = "/api/worker")
public class WorkerApi {
 
    @Autowired
    private WorkerService workerService;
 
    @PostMapping
    public int add(@RequestBody Worker worker) {
        System.out.println("------>>> add worker: " + worker.getName());
        return 9;
    }
 
 
    @PutMapping
    public boolean update(@RequestBody Worker worker) {
        System.out.println("------>>> update worker: " + worker.getName());
        return true;
    }
 
 
    @GetMapping("/{id}")
    public Worker get(@PathVariable("id") int id) {
        System.out.println("------->>> get worker: " + id);
        return workerService.get(id);
    }
 
 
    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable("id") int id) {
        System.out.println("------->>> delete worker: " + id);
        return true;
    }
 
}

这个接口里的4个方法覆盖了平时常用的四种Http请求方案,并将请求结果用一个统一的 ResultWrapper 类进行了封装(关于如何封装请求结果请参考上一篇文章 SpringBoot Controller返回值封装 )。

然后是单元测试方案。这里有两个超类: TestBase ApiTestBase 。前者用来对普通的注入实例进行测试,后者主要用来对Api接口进行测试。

TestCase 类内容如下:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestBase {
}

通过类内容可以看到,在测试中会启动一个内嵌的WebServer来加载Spring Context。这样的测试比较重一些,不过也和实际使用场景更一致一些。

ApiTestBase 类继承了 TestBase 类。在这个类里引用了 TestRestTemplate 的实例执行具体的接口调用,此外还定义了一些公用方法来减少使用时的代码量:

    @Autowired
    private TestRestTemplate restTemplate;
 
 
    protected abstract String parentPath();
 
 
    protected <T> Object testPost(String path, T param) {
        path = buildPath(path);
        System.out.println(toJson(param));
        ResponseEntity<ResultWrapper> response = restTemplate.postForEntity(path, param, ResultWrapper.class);
        ResultWrapper w = response.getBody();
        return getResponse(w);
    }
 
    protected <T, R> R testPost(String path, T param, Class<R> tClass) {
        Object r = testPost(path, param);
        String json = toJson(r);
        return fromJson(json, tClass);
    }
 
    protected <T> Object testPut(String path, T param) {
        path = buildPath(path);
        System.out.println(toJson(param));
        ResponseEntity<ResultWrapper> response = restTemplate.exchange(path, HttpMethod.PUT, new HttpEntity<T>(param), ResultWrapper.class);
        ResultWrapper w = response.getBody();
        return getResponse(w);
    }
 
    protected <T, R> R testPut(String path, T param, Class<R> tClass) {
        Object r = testPut(path, param);
        String json = toJson(r);
        return fromJson(json, tClass);
    }
 
    protected Object testGet(String path) {
        path = buildPath(path);
        ResponseEntity<ResultWrapper> response = restTemplate.getForEntity(path, ResultWrapper.class);
        ResultWrapper w = response.getBody();
        return getResponse(w);
    }
 
    protected <T> T testGet(String path, Class<T> tClass) {
        Object r = testGet(path);
        String json = toJson(r);
        return fromJson(json, tClass);
    }
 
 
    protected Object testDelete(String path) {
        path = buildPath(path);
        ResponseEntity<ResultWrapper> response = restTemplate.exchange(path, HttpMethod.DELETE, null, ResultWrapper.class);
        ResultWrapper w = response.getBody();
        return getResponse(w);
    }
 
 
    protected <T> T testDelete(String path, Class<T> tClass) {
        Object r = testDelete(path);
        String json = toJson(r);
        return fromJson(json, tClass);
    }
 
 
    private String buildPath(String path) {
        String p = parentPath();
        if (!p.endsWith("/") && !path.startsWith("/")) {
            return p + "/" + path;
        } else {
            return p + path;
        }
    }
 
    private Object getResponse(ResultWrapper wrapper) {
        System.out.println(toJson(wrapper));
 
        Assert.assertNotNull(wrapper);
        Assert.assertEquals(HttpStatus.OK.value(), wrapper.getCode());
 
        return null == wrapper.getResult() ? "" : wrapper.getResult();
    }

这里为每种请求都定义了两个方法,以根据需要返回不同形式的返回值。

看下是怎样使用的:

public class WorkerApiTest extends ApiTestBase {
 
    @Override
    protected String parentPath() {
        return "/api/worker";
    }
 
 
    @Test
    public void add() {
        Map<String, Object> param = new HashMap<>(2);
        param.put("name", "zhyea.com");
        param.put("age", 5);
 
        Integer id = testPost("", param, Integer.class);
        Assert.assertEquals(9, id.intValue());
    }
 
 
    @Test
    public void update() {
        Map<String, Object> param = new HashMap<>(2);
        param.put("id", 9);
        param.put("name", "chobit.org");
        param.put("age", 5);
 
        Boolean r = testPut("", param, Boolean.class);
        Assert.assertTrue(r);
    }
 
 
    @Test
    public void get() {
        Worker w = testGet("/1", Worker.class);
        Assert.assertEquals(33, w.getAge());
    }
 
 
    @Test
    public void delete() {
        Boolean r = testDelete("/9", Boolean.class);
        Assert.assertTrue(r);
    }
}

就这样。代码已经传到了GitHub上,有需要请自行查阅: GitHub / zhyea

原文  https://www.zhyea.com/2019/12/02/springboot-base-06-api-unit-test-practice.html
正文到此结束
Loading...