写一个测试框架|Java 开发实战

一、前言

需求:需要一个测试框架,能来测试一个 IM 系统,同时适配当前业务逻辑。

测试那用 Jmeter 喽,Jmeter 也能测试 websocket,干嘛要自己开发一个测试框架啊?

最重要的一点:TCP 协议是自定义的,用 Jmeter 等框架,无法很好集成。 PS:当然对 Jmeter 不够深入。

那么面向一个 IM 系统,其对应的测试框架,需满足:

  1. 具有连贯性,上下文:在保持连接情况下,执行一定业务逻辑处理,例如:发消息、加群、加好友等
  2. 易接入:开发对接代码简单
  3. 生成多种不同报告
  4. 可扩展

那么,那就试试呗。

二、v1 版本

初期思考,这种自动化测试,需要满足:

  1. 自动执行:启动之后所有测试用例都能执行,一个测试用例失败不能影响其他测试用例
  2. 可以输出定制的报告:控制台打印、生成文件
  3. 足够简单:容易集成业务,可以开发,并且运行速度快

思考过程

思考过程:

  1. 测试用例应该是一个对象
  2. 测试用例应该相互隔离
  3. 如何运行多个测试用例?
  4. 如何保存测试用例运行时出现的异常?

(1)测试用例应该是一个对象

运行:是一种能力,那么可以抽象为接口 interface

public interface Test {

    void run();
}

测试用例应该是一个对象,每个用例都是从一个模板出来的:

具有属性:用例名 name

public abstract class TestCase implements Test {
    protected String name;
    
    public TestCase() {
    }

    public TestCase(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
     
    }
}

(2)测试用例应该相互隔离

测试用例应该是相互隔离的:

  1. 一个用例的运行不应该影响另外一个用例
  2. 运行测试用例之前需要一些准备工作
  3. 运行测试用例之后需要一些清理工作

引出设计模式中 - 模板方法模式,代码如下:

public abstract class TestCase implements Test {
    protected String name;
    
    public TestCase() {
    }

    public TestCase(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        setUp();
        
        try{
            runTest();
        } finally {
            tearDown();
        }
    }
    
    protected void setUp() throws Exception {}
    protected void runTest() throws Exception {}
    protected void tearDown() throws Exception {}
}

(3)如何运行多个测试用例?

目前为止,此框架每次只能运行一个自定义的 TestCase

那能不能:

  1. 能不能 “透明” 运行多个?
  2. 调用方不用关心调用是一个还是多个?

这块就用到了设计模式中 组合模式,对应类图,如图:

写一个测试框架|Java 开发实战

代码如下:

public class TestSuite extends TestCase {

    private final List<Test> fTests = new ArrayList<>();
    
    @Override
    public void run() {
        for (Test test : this.fTests) {
            test.run();
        }
    }
    
    public void addTest(Test test) {
        this.fTsets.add(test);
    }
}

实际运行,例如:

TestSuite testSuite = new TestSuite();
testSuite.addTest(new CalculatorTest('test1'));
testSuite.addTest(new CalculatorTest('test2'));

TestSuite ts = new TestSuite();
ts.addTest(new CalculatorTest('test3'));
testSuite.addTest(ts);

testSuite.run();

(4)如何保存测试用例运行时出现的异常?

测试失败,代码抛出异常:Error

这时候,引入:TestResult 这个类,来收集结果。

重要的 TestResult 如下:

public class TestResult {
    private List<TestFailure> errors;

    ... ...
    // 重要:
    void run(final TestCase test) {
        startTest(test);
        try {
            test.doRun();
        } catch (Throwable e) {
            addError(test, e);
        }
        endTest(test);
    }

    private void startTest(Test test) {
        int count = test.countTestCases();
        testCount += count;
    }

    private void endTest(Test test) {}
}

其他对应的修改,代码如下:

// Test.java 如下:
public interface Test {
    void run(TestResult tr);
    int countTestCases();
}


// TestCase.java 如下:
public abstract class TestCase implements Test {

    ... ...
    // 重要:
    @Override
    public void run(TestResult testResult) {
        testResult.run(this);
    }

    ... ...
}

那么总结下,目前的调用过程吧:

写一个测试框架|Java 开发实战

小结

目前为止,用到了哪些设计模式:

每个思考步骤,对应一个设计模式。

  1. 命令模式:表达一个测试用例,即 Test
  2. 模板模式:完成数据的准备和清理,setUp() 和 tearDown()
  3. 组合模式:屏蔽一个和多个的差别,List<Test>
  4. 收集参数模式:利用参数收集信息,隔离测试用例和测试结果

 

细心的同学已经发现,这不就是阉割版的 Junit 嘛!!!

是的。仿照了 Junit 3Junit 4 开始又改版了。

如下图,是 junit3 的框架:

写一个测试框架|Java 开发实战

但 Junit 有个重要思想:约定优于配置。

即,Junit 会加载 test 开头的方法作为测试用例。 现在看起来很简单,但想法很重要啊!!!

如果觉得文章对你有帮助,麻烦 点赞、评论、收藏 你的支持是我最大的动力!!!

最后小编在学习过程中整理了一些学习资料,可以分享给做java的工程师朋友们,相互交流学习,需要的可以加入我的学习交流群 716055499 即可免费获取Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)

作者:dingyu002

来源:dinyu002

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

上一篇:oi时经常遇到的问题(不断更新)


下一篇:httprunner 3.x学习2 - 测试用例结构(testcase)