业务经常涉及到Excel文件的导出,后台给前端提供导出服务接口,不可能自己都没测试过,就提供给前端调用。最好的方案就是程序员编写单元测试,可以说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。

##单元测试
java 常用的单元测试框架是Junit,利用测试框架可以使我们在编写测试用例的过程更加便捷优雅。通过测试用例,我们只需要关注:对于特定的输入,被测对象的返回是否征程,下面是涉及到的相关类。

controller类提供导出接口

    @GET
        @Path("/{version}/exportRecordDetail")
        @ApiOperation(value = "导出电子支付结算明细")
        public String exportRecordDetail (@Context HttpServletResponse response, @BeanParam SingleWithdrawReq request){        
            //...
            response.setContentType("application/x-download");
            // 获取模板文件
            String templateFile = "/export/AccountRecordDetail.xlsx";	    
            try (InputStream is = this.getClass().getResourceAsStream(templateFile);
                 OPCPackage pkg = OPCPackage.open(is);
                 XSSFWorkbook workbook = new XSSFWorkbook(pkg)) {
                outputFile = URLEncoder.encode(outputFile, "UTF-8");        
                //...
                workbook.write(response.getOutputStream());
            } catch (IOException | InvalidFormatException e) {
                //... 
            }
            return null;
        }

核心代码: workbook.write(response.getOutputStream()); 这一句是指将文件写到输出流里,用到了HttpservletResponse 接口,因此在测试用例里,我们并不能直接实例化HttpservletResponse,因而也就不能作为参数传递到controller层。

那怎么办呢?首先想到了引入mock方法。Mock的引入,可以帮助我们构建比较难构造的Object,这些Object 通常有很多以来,在单元测试中构造出这些对象通常花费比较大的成本。

Mock 单元测试

@Test 
public void exportService() {
    SingleWithdrawReq request = new SingleWithdrawReq();
    request.setParkId(12345);
    ParkingBalanceAccountResources resource = new ParkingBalanceAccountResources();
    // Mock HttpServletResponse 实例
    HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
    // 定义一个输出的本地文件
    File file = new File("output.xls");
    try (ServletOutputStream sos = new MockServletOutputStreamUtil (file)) {
        //用MockServletOutputStreamUtil 代替ServletOutputStream
        Mockito.when(response.getOutputStream()).thenReturn(sos);
        resource.exportRecordDetail(response, request);
    }
 }

单元测试思路: 将输出流写到一个临时文件里,但是在case里我们如何传入一个response并且得到输出流呢?

##工具类:构造ServletOutputStream

public class MockServletOutputStreamUtil extends ServletOutputStream {

    private FileOutputStream fos;    //定义文件输出流
    public MockServletOutputStreamUtil(File file) {
        try {
            this.fos = new FileOutputStream(file);    // 将输出流指向文件
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public boolean isReady() {
        return true;
    }
    @Override
    public void setWriteListener(WriteListener writeListener) {
        throw new RuntimeException("do not support this method");
    }
    @Override
    public void write(int b) throws IOException {
        fos.write(b);
    }
    @Override
    public void close() {
        try {
            fos.close();
        } catch (IOException e) {
            throw new RuntimeE
xception(e);
        }
    }

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐