• 因为最关键的是 data,其他都是已经写好不变的,每次报告的不同,就只有 data,报告页面是根据 data 来展示的,所以你看到的手工生成报告的其他文件,都是可以公用的,如果要每次独立报告, 最简单粗暴的做法就是每次新建一个文件夹,把固定的部分复制到文件夹,然后把本次的 data 放进来,就是一份新的报告

  • 抱歉,最近比较忙,没有及时看到。
    前端的 “执行” 是通过 http 调用了后端执行测试任务的接口,将接口名称、用例名称、测试环境名称作为入参。
    测试任务执行接口后面的业务逻辑,大概有一下几步:

    1. 首先后端已经把接口签名、接口入参组装、接口调用、接口响应获取和解析、数据校验,都封装好了,在一个自定义的继承ITest的 testcase 类里面,将数据作为 testcase 的入参,将数据预置放在@BeforeMethod,签名、调用、校验放在@Test,数据清理放在@AfterMethod,只要有相应的数据,就可以完成测试.(在上面 13 楼有这个 testcase 类的样例)
    2. 根据入参中的接口名称得到被测接口方法名,根据入参中的用例名称得到该用例调被测接口时的入参、预期结果和校验方式等信息,根据环境名称得到接口中心域名,这些是一条用例里面要调接口所需要的数据。如果是批量执行,则得到一个数据集合
    3. 将前面得到的数据,用 TestNG 的 Factory + DataProvider,生成一个在内存中存在的虚拟testng.xml,传给 testng,然后调用run()方法,就开始真正执行用例了
    4. 然后生成报告,可以自己选择,最后返回结果,就用响应的 tesgng 监听器即可,我这里只用了TestListenerAdapter,然后要在接口返回简单的测试结果数据的话,就从TestListenerAdapter拿,比如获取失败用例数tla.getFailedTests().size()。把这些数据作为点击 “执行” 之后的响应返回,这样前端有反馈,而且方便在 CI 中使用。也就是一次测试任务执行后,有两类结果,一是 html 形式的测试报告,二是接口返回里面的简要执行结果数据

    最后总结一下,整个过程分为数据获取、用例集组装、接口调用和校验、生成报告、执行结果数据反馈。这里每一步都是独立封装

  • 非常感谢,都是很实际的问题,我并没有做好。

    版本问题

    这可能是个隐患,我先想想,感谢。 平台设计时没有考虑,目前平台运行近一年,暂时没有遇到这方面的需求,所以并没有相关的处理。

    前端的问题

    还没有遇到。我不清楚对于从 HTTP 层进行的测试,case 里添加什么东西需要不断的添加前端代码,平台除了新增功能有添加新的页面外,原有页面基本只进行过文案优化和添加几个函数进行易用性优化。
    我认为一个系统需要有成体系的设计,它并不能一成不变就满足新增的需求,但是能够以较小的去实现。同时一个系统要有自己的边界,确定自己真正要做的是什么,不管是业务范围、还是 UI/UE,特别是在资源有限的情况下,更是如此。我本身前端水平很初级,所以尽量考虑怎么简单,中间也考虑过一些感觉更好用更酷炫的特性,非要做也能做出来,最终还是放弃了,不然或许就向你说的这种情况发展了。

    调试的问题

    平台并没有完全解决,不过没有成为日常使用中的阻碍。
    - 就接口测试来说,基本还没问题,平台有提供在线调试用例,会将用例所调接口的完整响应格式化展示在界面上,给出用例定义的校验项的结论。如果接口根本不通,会有相应的反馈,那就需要自己去排查原因了。
    - 而在业务场景测试这里,确实没有完全在线解决。因为一个业务场景可能包括很多次业务调用,还涉及一些逻辑判断,调试的需求比接口测试多很多,我这里也只是如果报错,界面展示报错信息,后台日志记录完整调用过程,因为本身是内部平台,所以这种折中办法基本能处理。另外因为是 HTTP 层的测试,业务场景里面基本也是业务调用,数据库操作,简单逻辑控制等,范围相对清晰,调试问题还不太严重。

    最后这个问题

    哈哈哈,这个问题确实很常见,我的做法是尽量保持简单,以及开放和分享,效果并不确定

  • 可以参考我#13 楼层的回复,从数据库把当次要执行的用例数据全部捞上来之后,运用 TestNG 的 Factory + DataProvider,然后用 TestNG 所支持的在内存中构建虚拟的 testng.xml,最后用如下方式执行:

    TestNG tng = new TestNG();
    tng.setXmlSuites((List<XmlSuite>) suites);
    tng.run();
    

    总体来说都是运用 TestNG 自己已有的特性,可以从 TestNG 官网去了解这些特性

  • 最近刚被公司提醒了,属于公司项目,不要私自传播😂 ,已经从 github 撤下来了。大概可以聊聊设计和思路

  • 嗯,这里有个前提,就是你的用例可以抽象出相同的逻辑,如果每个用例的逻辑差别很大,那为每个逻辑写单独的代码是很难避免的。
    不过即使你不能为所有用例抽象出同一个逻辑,那抽象出几类逻辑应该是可以的,这样的话,抽象出几个逻辑,就对应几个 test 类。

    我用的解决方案:TestNG 的 Factory + DataProvider。
    本文的平台主要是做接口层面的测试,可以抽象出相同逻辑,比如:前置:数据库数据准备 + 用例:准备参数 - 调用接口获取响应 - 响应校验 + 后置:数据库数据清理,每个步骤都做了封装,所以只要用 TetNG 的这两个特性做数据驱动就可以实现只需要一个 tsetcase 类。最后在用例组织的时候,借助 XmlSuite/XmlTest 等类以 Programatically 的方式构造当次的执行计划 (也就是构造临时的 testng.xml)。

    Factory + DataProvider 中文英文资料都有,在内存里构造 testng.xml 这个也是有资料的,你搜一下吧。下面是我的一个 testcase 类的简化版,去掉了一些无关的信息,以供参考。

    public class APITestNGCase extends DefaultTestClass {
        private static final Logger logger = LoggerFactory.getLogger(APITestNGCase.class);
        //整个过程所需的参数较多,所以对入参进行了封装
        private TestCaseDataEntity entity;
    
        public APITestNGCase(TestCaseDataEntity entity) {
            super(entity.getCaseName(), entity.getCaseOrder());
            this.entity = entity;
        }
    
        @BeforeMethod
        public void setUp() {
            if (isNotBlank(entity.getDbSetUp())) {
                TestCaseConditionProcess.getInstance().setUp(entity.getDbSetUp(), entity.getJdbcTemp(), entity.getTranTemp());
            }
            logger.info("Testcase : {}  beforeMethod completed ", this.caseName);
        }
    
        @Test
        public void IntegrationTest() {
            //参数准备-调用接口-获取响应
            Response response = RestExecutor.getResponse(RestExecutor.init(params),entity.getApiDomain(), entity.getApiPath(), entity.getHttpMethod() );
            //响应校验
            RestExecutor.validate(response, entity.getStatusCode(), entity.getValidationPaths(), entity.getValidationTypes(),
                    entity.getExpectedValues());
            //数据库校验
            if (isNotBlank(entity.getDbCheck())) {
                TestCaseConditionProcess.getInstance().checkFromDb(entity.getDbCheck(), entity.getJdbcTemp());
            }
        }
    
        @AfterMethod(alwaysRun = true)
        public void tearDown() {
    
            if (isNotBlank(entity.getDbTearDown())) {
                TestCaseConditionProcess.getInstance().tearDown(entity.getDbTearDown(), entity.getJdbcTemp(), entity.getTranTemp());
            }
            logger.info("Testcase : {}, afterMethod completed ", this.caseName);
        }
    
    }
    
  • 嗯,缓存方面的处理现在没有做,因为还没有实际需求,目前的几个产品线虽然都有缓存,但是在当前的自动化用例里面还没有设计。谢谢提醒,我先考虑一下,一遍后续有实际需求来了能更好的解决。
    强业务的接口组合测试,应该是很多接口自动化实施过程中难以良好解决的问题,我做的尝试是放在在线代码编辑这部分来处理的,就是页面上提交一个继承指定抽象类的代码,自己在里面组织业务场景的各个步骤,用例运行时动态编译,前面漏了这部分的截图,在 UI 概览补上了

  • 嗯,都是些很实际的问题,比如入参是否固定,我们公司有团队之前的自动化框架就是固定参数,因为他们当时的目的只是测试产品对外的接口,这些接口的参数都是固定的几个 K-V,比如 timestamp,method,data 等等。下面就对你的四个问题说一些我的思路。

    问题 1

    首先目前平台上的业务接口来自于多个不同类型的产品,case 入参各有不同,并不是固定的。其次是平台用例接受的入参也没有限定。可以从最后一节的第二张截图看到,我在平台把入参分为了三种类型:
    - Path 参数 - 如果产品 API 遵循 RESTful 风格,就很容易见到如/user/{id}这样的操作特定资源的形式,这里的 id 就归为 Path 参数;
    - Form 参数 - 就是场景的表单/K-V 键值对,这不管是遵循 RESTful 风格还是随意的风格,都非常场景,一些简单的业务基本这样的简单参数就满足需求了;
    - Body 参数 - 放在 request body 中传输的内容,通常是 Json 串,一些比较复杂的业务,参数结构比较复杂,可能需要这样的形式。path 参数、form 参数都不限制数量,只要按规则填写让平台可以解析即可(这和一些开放平台如淘宝开放平台接受可选参数的方式是类似的,用一个字符串来接受用户按规则填写的不定数量的 K/V,后台按规则解析。),Body 本身是直接接受一个字符串,不存在参数数量的问题。

    问题 2

    目前公司线上是全站 HTTPS,所以数据传输没有再做别的加密。所以目前平台没有做加解密方面的特性,不过这个问题不大,先了解自己产品的加密传输过程,做的时候用例里面还是明文写入参和其他要素,后台用一个基础服务来算摘要、加解密这些,应该没有特别大的障碍。

    问题 3

    上下文关联接口,这恐怕是多数解决方案都比较头疼的问题,要尽量简单易用,然而又想尽可能的满足更多更复杂的需求。可以从两方面看:

    1. 分析当前阶段有没有必要,如果是比较纯粹的接口测试,就应该尽量单独对每个接口进行测试,优秀的接口设计也应该能适应这样的测试,那这时候有数据需求,就考虑预置数据来解决,我这边的设计是通过填写直接的 SQL 语句支持数据预置和清理,就放在用例的 setUp 和 tearDown 里面,支持多条 SQL 语句,目前平台上有个产品就是这样来实现所有用例独立的,这在实施的时候需要对数据进行一些必要的规划,可能需要一些思考,但不管是怎样的自动化方案,要做好数据预置,必然需要进行规划的。

    2. 如果业务特殊,接口间依赖确实难以解除,或者数据预置的成本太大,或者本来的需求就是做一些业务流程冒烟测试,我在平台给出的解决办法是,自己用代码在线自由组装任何场景。

      可能看到这里有同学会想到,看,还是要写代码。我也很无奈啊,像这种需要多个接口组合,要从前一个接口获取预先不确定的值传给下一个接口的复杂用例,如果非要通过设计来实现无代码,以我的经验和见识,需要投入太多,而且设计出来的仍然有这样那样的条条框框,既然是要解决复杂场景,就是想要应对尽可能多的,所以最终我选了现在这样的设计。

      但这里的自己用代码,其实只是实现一个接口,像写脚本一样写一些简单的结构化代码就可以应对 99% 的场景测试,一般就是点条件判断,最多加点循环,数据库操作都已经封装好了,只要传 SQL 语句作入参即可,请求组装,HTTP 调用,响应数据获取,响应体的 Json 解析,都是现成的,编译也是平台运行时动态编译的,小伙伴只要照着样例就写出源码即可,甚至一些常用的包,也在抽象类里面引入了,在线写的代码里面只有个别不常用的包需要自己引入,这个水平在公司每个组都有能胜任的。这个特性在平台培训的时候确实需要多做一些说明,但也仅此而已,现在平台上做业务流程冒烟自动化的产品,就是用这个特性来做的,只要协助小伙伴实现一两个场景,后续基本就能自己解决了。

    问题 4

    就我这边的设计来说,对于常规的接口自动化用例,确实是需要预先知道用例的确切预期结果,包括数据库校验,也是需要给出明确的条件。
    而就前面提到的通过在线代码实现的场景测试,则可以在部分场景下实现不需要提前知道结果,最典型的就是一些查询接口,比如平台上有个产品一个业务查询消费明细、查询账单金额等 case,就没有考虑预置数据,而是用例执行时先从数据库查到结果,然后和接口返回的结果进行对比,用例设计这不需要预先知道每次执行时,实际的金额。

    一下写的有点多,总之目前我的实现,只到这个程度。

  • 平台后端代码本身倒是基于 github 管理的,平台的多数功能,也具备通用性,但一来对于身份认证部分,各公司肯定会有所不同,而来这是一个系统,而不是一个基础框架,之前设计的时候没有考虑开源方面的事情,所以算是没有开源计划。有时间的话后面会拿一些关键逻辑放到后续文章。

  • 嗯,难的从来不是 coding。
    我也经历过自己做的东西远没有达到自己预期的情况。我觉得要做这类平台,首先自己要对业务测试本身有相当的了解,然后,公司产品比较多的,需要进行一些归类和分析,每个类型的系统至少熟悉一个产品的业务特性和系统设计风格,这对做出一个能顺利落地的平台是很重要的,因为这些将影响首版的设计,如果首版不能成功落地,不管是对使用者还是对自己,都有很消极的影响。
    我自己来说,对公司多数产品业务都有一定了解,系统的接口设计风格也都比较清楚,请求、返回、身份验证这些细节等等,最初设计的时候就会考虑如何适用于各种不同设计风格,需要考虑在表现层进行一些针对性的设计来增强易用性,最终做出来的平台目前算是顺利落地了,有的用来做对外接口的自动化,有的用来做业务冒烟,也有纯后端平台的同学用来做业务测试。中间也有一些不满足需求的地方,这种时候我会尽量快速响应,并收集一些他们的想法。