自动化测试框架分类与思考

自动化测试一直是敏捷开发和敏捷测试的重要基石,也是DevOps和CI/CD必不可少的组成部分。由于不同项目的测试需求不同,以及各种不同的限制,导致需要的自动化测试框架和工具也不同。比如很多金融和能源类的企业就倾向于选择收费的企业级自动化测试框架或者工具,而新型互联网企业则倾向于开源免费的自动化测试框架或者工具,或者基于它们进行二次定制开发,或者重新开发适合自己的自动化测试框架、工具或者平台。

我之前写过一篇文章——《自动化测试框架Cucumber和RobotFramework的实战对比》仅仅针对两种自动化测试框架进行了讨论,却引发了大量的讨论,由此可见业界对于自动化测试框架存在很多不同的理解和争议。在我看来,没有任何一个自动化测试框架是银弹,并且适合所有类型的测试,所以“如何选择一款适合自己的测试框架”变成为了一个首要问题。我将自动化测试进行了简单的分层,见下图。

自动化测试架构分层图

其中测试库和被测系统紧密相关,所以可以选择的范围不是很大,也很难进行统一分类。而测试框架与被测系统关系并不紧密,而是和技术栈,开发流程与组织管理等关系紧密相关,并且种类繁多,可选择范围很多,所以选择也相对比较困难。

因此对测试框架进行统一分类可以更好的帮助团队选择适合自己的测试框架,从而更好进行自动化测试开发。

本文根据自动化测试用例的呈现方式和管理方式将其分为四种类型:函数型,单领域语言型,多领域语言型以及富文档型。

四个类型:

函数型

函数型自动化测试框架是第一代自动化测试框架,也是最轻量的测试框架。它只是通过函数的方式来定义测试用例,并且通过管理这些函数的调用来管理测试用例,从而快速的实现自动化测试,比如xUnit等。

例子JUnit:

public class DemoTest {
  @Test
  public void testAddWithTwoNumbers() {
    //测试实现代码
  }
}

函数型自动化测试框架由来已久,开发快速,运行稳定。虽然它相对简单与轻量,但是也存在缺点:很难通过函数名来描述测试用例的内容和细节,并且不方便对测试用例进行单独管理,因为测试用例的描述函数名和测试实现通常都在一起。

单领域语言型

由于函数型的自动化测试框架很难通过函数名去描述一个测试用例的内容。为了更清晰和容易的描述测试用例,就出现了单DSL型的自动化测试框架,比如RSpec,Jasmine,Mocha等。

例子Jasmine:

describe("The add function of the calculator can add two numbers", function() {
  it("should get the sum after add two numbers", function() {
    //测试实现代码
  });
});

单领域语言型可以通过自然语言或者关键字形式的领域语言来描述测试用例,从而以一种更加易读和理解的方式来描述测试用例。但是每个测试用例只用一句DSL语言,并不能很好的描述测试用例和被测场景,不易形成一套好的活文档。由于它的测试用例与测试实现通常也是在一起的,所以也不方便对测试用例进行单独管理。

多领域语言型

由于单DSL型框架中对于每个测试用例只能使用一句DSL来描述,并不能很好的体现测试用例场景,比如测试的前提,行为和结果等。为了能在测试用例层更为清晰的描述测试用例的行为和测试数据等型信息,出现了多领域语言型的自动化测试框架,比如Cucumber,JBehave,SpecFlow,RF等。

例子Cucumber:

测试用例代码

Feature: The add function of the calculator can add two numbers
  Scenario: add two numbers
    Given there are two numbers <Number 1> and <Number 2>
    When add these two numbers 
    Then should get <Sum> of two numbers
  Examples:
    | Number 1 | Number 2 | Sum |
    | 1        | 2        | 3   |
    | -1       | 2        | 1   |

测试实现代码

Given(/^there are two numbers$/) do
  //测试实现代码
end
When(/^add two numbers together$/) do
  //测试实现代码
end
Then(/^should get sum of two numbers$/) do
  //测试实现代码
end

多领域语言型的框架可以通过多句或者多个关键字的领域语言来描述一个特定的场景,使得测试用例更容易阅读和理解,并且比较容易做成一套活文档系统。由于测试用例和测试实现是分离的,还可以对测试用例进行独立管理。

但是缺点也是比较明显的,开发、管理和维护成本较高,并且如果没有业务分析或者产品人员等非技术人员参与协作开发,那么它的投入产出比就很低,大家往往会认为它是事倍功半。

富文档型

对于一些场景十分复杂,需要通过富文档的方式来描述软件测试场景,甚至需要一些业务流程图或者系统用户界面等,比如Concordion,Fitnesse,Guage等。

例子 Condordion:

测试用例代码,其中包含部分测试代码,比如断言等,其中concordion.css使用的是官方样例代码

<html xmlns:concordion="http://www.concordion.org/2007/concordion">
<link href="concordion.css" rel="stylesheet" type="text/css" />
  <body>
    <h1>Test Demo</h1>
    <p> Test the add function of  the calculator can add two numbers </p>
    <p> The Caculator:</p>
    <div> <img src="./Calculator.png"/> </div>
    <div concordion:example="add">
      <h3>Examples</h3>
      <table>
        <tr>
          <th>Number 1</th>
          <th>Number 2</th>
          <th>Sum</th>
        </tr>
        <tr concordion:execute="#sum = addWithTwoNumbers(#number1,number2)">
          <td concordion:set="#number1">1</td>
          <td concordion:set="#number2">2</td>
          <td concordion:assert-equals="#sum">3</td>
        </tr>
        <tr concordion:execute="#sum = addWithTwoNumbers(#number1,number2)">
          <td concordion:set="#number1">-1</td>
          <td concordion:set="#number2">2</td>
          <td concordion:assert-equals="#sum">1</td>
        </tr>
      </table>
    </div>
  </body>
</html>

测试用例呈现文档:

测试用例中的函数实现代码:

@RunWith(ConcordionRunner.class)
public class CaculatorFixture {
  public String addWithTwoNumbers(String number1, String number2) {
    //测试实现代码
  }
}

(注:虽然说最新版的Concordion已经支持MarkDown了,从而降低了一些开发成本,但是其对MarkDown的特性支持有待增加。所以如果需要更为丰富的文档形式,仍然需要使用HTML来开发测试用例。)

富文档型的框架比多领域语言型拥有更为丰富的文档,更容易阅读和理解,从而能做成说明书式的活文档,使得所有角色的人都能审阅。并且其测试用例和测试实现也是分离的。但是当前业界存在的富文档型测试框架的易用性和协作性都还不是很好,导致其开发,管理和维护成本相比前三种是最高的。并且当没有其它各个角色来协同开发,管理和维护时,其投入产出比也是最低的,所以它在行业中的使用率也是很低的。这类测试框架在易用性和协作性方面还有很大的发展空间,并且也是自动化测试框架和活文档系统的一个重要的发展方向。

思考与选择

自动化测试的代码实现层一般是与编程语言强相关的,而主流的编程语言比较少,所以选择比较容易:一般建议选择团队大部分成员都熟悉的编程语言(这样可以促使整个团队来对自动化测试进行开发和维护)或者是有特定测试库的编程语言(比如需要使用Scapy时就只能选择基于Python的自动化测试框架)。当确认自动化测试开发语言后,真正的问题是如何在如此众多的自动化测试框架里面选择合适自己的自动化测试框架。选择方法可以根据以上四种类型来进行选择,从而缩小选择范围。

  • 如果团队只是需要快速实现自动化测试,没有知识的传递问题,也不需要与业务分析和产品经历等非技术人员进行协作开发时,可以选择函数型自动化测试框架。
  • 如果为了解决知识传递问题,让测试用例更可读和易懂,并且没有非技术人员参与协作开发,这时可以选择单领域语言型。
  • 如果为了进一步解决和非技术人员协作开发的问题,并且想有一套简版的活文档,可以选择多领域语言型自动化测试框架。
  • 如果为了让测试用例拥有更为丰富的表现力,比如包含一个流程图来说明被测场景的流程,或者使用不同的格式或者表格来描述用例的细节,以及拥有一套丰富的活文档,这时就可以使用富文档型。不过由于当前的富文档型测试框架在编写用例时需要一定的技能,所以非技术人员很难直接参与协作编写。并且其编写以及维护成本更高,可能使得自动化测试开发人员使用的意愿也不是很高。参考自动化测试工具选项金字塔:

当确认了测试框架类型之后,比如只有一个可选项(Java->函数型->JUnit),那么就直接使用了,但是如果存在多选项(JavaScript-> 单领域语言型->Jasmine vs Mocha),就还需要对其进行深入比较,从而最终选择自己适合的自动化测试框架。

扩展阅读:


更多精彩洞见,请关注微信公众号:思特沃克

Share
刘冉

刘冉

现任ThoughtWorks资深软件质量咨询师,超过14年软件开发和测试工作经验。最熟悉的领域是嵌入式系统开发、Linux系统开发、各种脚本、测试工具、自动化测试系统开发、以及敏捷测试。《代码管理核心技术及实践》作者。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.