阅读 320

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

这是我参与更文挑战的第10天,活动详情查看: 更文挑战

本文正在参加「Java主题月 - 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. 调用方不用关心调用是一个还是多个?

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

test.png

代码如下:

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);
    }

    ... ...
}
复制代码

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

Test时序图.png

小结

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

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

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

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

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

如下图,是 junit3 的框架:

2021-06-1017-09-58.png

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

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

文章分类
后端
文章标签