目录

测试框架

测试用例编写

痛点

设想

方案设计

技术手段

为啥不用testng的@BeforeMethod

参考


测试框架

junit

testing

测试用例编写

示例:测试订单的提交方法

  1. 准备Product商品对象
  2. 准备OrderItem行项对象,并赋值Product
  3. 创建Order对象,并给赋值OrderItem
  4. Order对象进行提交,并生成OrderNo
  5. 查询Order数据并Assert
  6. 调用Order对象的delete方法
  7. 调用OrderItem的delete方法
  8. 调用Product的delete方法

如下图所示:

gradlew test输出测试结果 test grid_testng

正真核心的逻辑只要就是提交一下,但这个逻辑会依赖其他逻辑。所以需要编写很多辅助逻辑,并在测试完成(成功或者失败)都需要清理生成的垃圾对象。 

gradlew test输出测试结果 test grid_junit_02

按照模型驱动DDD,UML的思路:

gradlew test输出测试结果 test grid_gradlew test输出测试结果_03

  1. 订单依赖 商品:没有商品就不可能生成订单
  2. 每个类都有一个生命周期,起点是创建,终点是删除
  3. 对象生命周期各阶段会有一个依赖关系:如人会经历“出生”“幼儿”“儿童”“少年”“青年”“中年”“老年”“拜拜”

痛点

编写测试用例会创建很多依赖对象,这些对象都需要在结束后回收,很容易遗漏,写起来工作量也挺大。

设想

  1. 依赖的对象:在方法钱,添加配置(实体,方法,几个)就可以获取到对象
  2. 对象回收:创建的对象可以自动回收,不需要手动调用方法或者接口来清除

测试如果是下面的就好了:

// 或者使用这种方式使用,注册后使用的方法不同罢了
@Service("Order")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= "classpath:bean.xml")
public class Order extends BaseObject {

    // 注册实体类信息
    protected Order(String objectName) {
        super("Order",Order.class);
    }

    @DependentObjects(objects = [
            /**
             * 依赖OrderItem实体,创建一个对象,名称前缀item,变量名位:item001,item002
             * 至于OrderItem依赖啥就在OrderItem.create方法中配置
            */
            {entity="OrderItem",objectNum=1,namePre="item"}
    })
    @Test
    public void create(){
        /**
         * 1. 根据名称或者Product类型获取
         * 2. 创建Order,并赋值item和product
         * 3. create的对象,在destroy方法中删除
         */
    }

    // 依赖的对象是Product,OrderItem
    @DependentObjects(objects = {"Product","OrderItem"})
    // 依赖的订单状态是create方法,即在create执行后再执行该方法
    @DependentOperate("Order.create")
    @Test
    public void commnit(){
        /**
         * 1. 根据名称或者Product类型获取
         * 2. 执行操作
         * 3. 执行后自动回收依赖的对象和使用规范中创建的对象
         */
    }

    // 清除对象
    @Test
    // 依赖的订单状态是create方法,即在create执行后再执行该方法
    @DependentOperate("Order.create")
    public void destroy(){
    }
}


public class OrderItem extends BaseObject {

    @DependentObjects(objects = [
            // 依赖Product实体,创建两个对象,名称前缀pro,变量名位:pro001,pro002
            {entity="Product",objectNum=2,namePre="pro"}
    })
    @Test
    public void create() {

    }

    @Test
    public void destroy() {

    }
}

方案设计

  1. 实体和测试类对应:按照实体和测试类对应方式,确定每个实体都有一个创建和删除的方法,这些方法都可能是调用后端接口实现
  2. 测试方法依赖类:通过主键的方法配置。
  3. 实体内状态依赖:配置依赖的上一个方法
  4. 测试用例启动时加载配置
  5. 在每个Case方法执行的时候,使用AOP创建根据配置的注解执行前置方法
  6. 前置方法产生的数据对象放在线程缓存中
  7. 方法执行后清理线程缓存中的方法

技术手段

配置加载

springboot+junit:@SpringBootTest @BeforeAll,或者使用构造方法

testng:@BeforeSuite

AOP切面

junit+spring:配置注解类型的AOP即可

testng:Ihookable监听器

对象创建和删除

每个对象都实现一个create方法,并存储到缓存中

执行完成后有AutoGc组件调用destroy方法进行回收

垃圾回收

根据依赖创建对象的顺序栈,回收

在当前方法执行完就开始执行

为啥不用testng的@BeforeMethod

  1. 方法级别和业务抽象不同:testing 可以实现代码级别beforeMethod,即:在这个方法之前执行啥,而咱们的组件更侧重于抽象到实体的相互依赖。
  2. 体现了领域驱动设计的方法论,对开发者的约束更强
  3. 实现了自动回收辅助的垃圾对象机制,而testng只是方法级别支持你每个方法之后预留锚点接口,没有实现自动垃圾回收机制

参考

TestNG简单的学习-TestNG运行 | 运行