53

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

 4 years ago
source link: https://www.zhyea.com/2019/12/02/springboot-base-06-api-unit-test-practice.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

以前写过关于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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK