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

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » springboot入门06 – 接口单元测试方案

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址