Grooy进行单测 少些bug非他莫属

367 阅读4分钟

单测的心得体会

单测是规范的软件开发流程中的必不可少的环节之一。再伟大的程序员也难以避免自己不犯错,不写出有BUG的程序。单测就是用来检测BUG的。Java阵营中,JUnit和TestNG是两个知名的单测框架。不过,用Java写单测实在是很繁琐,mock各种参数需要大量时间,所以单测试往往被认为鸡肋,会耗费很多时间。最近实习的过程中遇到一些坑,发现养成一个良好的单测习惯对于系统化开发是很有必要的。

背景介绍:

目前开发的项目基本都是基于dubbo进行远程服务调用。由于自身项目比较复杂(主要在于内部实体类之间转换过于复杂以及系统依赖服务较多导致调用链过长),造成自身无法简单的使用postman这些工具进行接口测试,且项目过大 内部开发往往需要将本地代码push上去再容器部署,一个小小的bug修改部署成本极大(往往要十五分钟)。最近写的第二个项目由于传参NPE 问题进行了十多次修改,可想而知部署成本极大。由此懂得单测的必要性。

我们部门主要使用spook+grooy进行单元测试。简单介绍如何使用grooy和spook实现简洁单元测试以及今天写单测遇到的一个坑。

原先代码中使用junit4配合Mockito 进行测试 发现对于特定条件判断往往代码量大,而且冗余。观摩以前的代码往往发现其中大部份测试都是对结果非空判断,如果遇到多条件的情况想要具体结果需要大量when() thenReturn();

spook进行单测好处是模版简单,代码简洁,对于特定数据可以清晰判断。

使用spook进行单测主要模版为 expect-where , when-then-thrown, setup-given-when-then[-where]

比如:这是最近写的一个例子可以简单的看作setup-given-when-then[-where] 这种形式

def "testFXSupplierName"(){
        setup:
        def shopClient = Mock(ShopClient)
        OrderSearchServiceImpl service = new OrderSearchServiceImpl()
        service.shopClient = shopClient
        when:
        List<OrderBO> orderBOList=[new OrderBO(xxxx:new xxxx(extra: ["xxxx":2],tags:["xxxx":true]))]
        OrderPageBO orderPageBO=new OrderPageBO(list: orderBOList)
        shopClient.queryShopMetaInfos(_) >> new ShopMetaInfosQueryResult(shopMetaInfos:[new ShopMetaInfo(xxxx: 2,xxxxx: "test")])
        service.supplierOrderPutSupplierName(orderBOList)
        then:
        orderPageBO.getList().get(0).getOrderMain().getExtra().get("xxxx")=="test"
    }

setup:建立要mock的实体类

given:建立返回的对象实体

when:将given中建立的对象实体赋值mock实体方法调用或进行方法调用

then:结果比较

where:多条件添加

这也是一个例子

@Unroll()  //这边是标记信息可以打标
    def 'presell_normal'() {
        setup:
        def tradeSku = new TradeSku(preSale: presell)
        def goods = new Goods(num: num, price: price)
        def icGoods = new IcGoods(depositType: depositType,
                                                        depositRatio: depositeRatio,
                                                        deposit: deposite)
        tradeSku.setIcGoodsPreSaleInfo(icGoods)
        expect:
        def value = TradeGroupConvertUtil.preSell(tradeSku, goods)
        value == res
        where:
        presell << [1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
        depositType << [0, 0, 0, 0, 1, 1, 1, 1, 0, 0]
        depositeRatio << [40, 20, 30, 80, 1, 2, 3, 4, 0, 0]
        num << [1, 2, 3, 4, 1, 2, 3, 4, 0, 0]
        price << [100, 100, 100, 100, 100, 100, 100, 100, 0, 0]
        deposite << [40, 20, 30, 80, 1, 2, 200, 4, 0, 0]
        res << ['40', '40', '90', '320', '1', '4', '300', '16', '', '']
    }

对于异常场景可以使用这种

def 'getpreSell_error'() {
        setup:
        def tradeSku = new TradeSku(preSale: presell)
        def goods = new Goods(num: num, price: price)
        def icGoods = new IcGoods(depositType: depositType,
                                                        depositRatio: depositeRatio,
                                                        deposit: deposite)
        tradeSku.setIcGoods(icGoods)
        when:
        TradeGroupConvertUtil.preSell(tradeSku, goods)
        then:
        def ex = thrown(Exception)
        expect:
        ex.message == errorMessage
        where:
        presell << [1, 1, 1, 1, 1, 1,]
        depositType << [0, 0, 0, 0, 1, 1,]
        depositeRatio << [-12, 0, 100, 120, 200, 300]
        num << [1, 2, 3, 4, 1, 2]
        price << [100, 100, 100, 100, 100, 100]
        deposite << [40, 20, 30, 80, null, 0]
        errorMessage <<
        ["xxxxxx异常信息", "xxxxxx异常信息", "xxxxxx异常信息", "xxxxxx异常信息", "xxxxxx异常信息", "xxxxxx异常信息"]

    }

这边就是主要使用的三个例子,需要异常抛出的情况不能和正常情况一起使用。需要区分为两个测试方法。

遇到的坑:

今晚写一个单测,仿照项目中原先的老代码写的。对于代码中mock使用的是Mockito注解方式,发现使用注解方式可以获取到这个类但是对于使用grooy 语法进行方法调用赋值一直为null但是注入的类不为空否则会报NPE问题。后来通过重新使用mock()注入解决这个问题。得出结论使用何种方式注入就要使用何种方式赋值。