精华内容
下载资源
问答
  • 自动化测试原则

    千次阅读 2018-12-03 16:22:01
    自动化测试原则 文章目录自动化测试原则自动化测试的目的单元测试 vs 集成测试单元测试和集成测试之间的区别由你来决定每一层级的测试如何对测试排优先级测试即代码DRY 数据驱动测试测试DSL通用测试DSL专用测试DSL...

    自动化测试原则


    转自Principles of Automated Testing

    自动化测试是编写可靠软件的核心部分;手工测试仅能测试一部分,人不可能像机器一样对某功能彻底测试,或始终如一的谨慎。某些人花费极多时间在工作和开源工程的自动化测试系统上,这个帖子描述了我对此是如何看的。哪些区别是意义重大的,而哪些并不是;哪些实践有区别哪些没有;构建关于任意软件系统自动化测试的一套统一的标准。

    或许我比大多数软件工程师更关注自动化测试。在上一个工作中,我开始关注和开始Selenium集成测试作为软件工程师开发过程的一部分,开发了静态分析测试以避免低级的错误和代码质量问题,让工程克服不可靠的测试灾难以使CI过程重新流畅。在我的开源成果中,例如,Ammonite或FastParse中,我所写的测试代码与主应用代码的比例基本上会达到1:1。

    有很多关于自动化测试实践的文章,如单元测试,基于属性的测试,集成测试,以及其它主题。毫不意外的,大多数你从互联网上获取的信息并不完整,有可能互相矛盾,或仅仅只能应用于某些特定工程或场景。

    这个帖子并不讨论特定的工具或技术,而是试图定义某种思考关于自动化测试的方式,可以广泛的应用于任何你所工作的软件工程。希望这能形成某种有用的基础,在你最终从日常开发中抽出注意力,开始关注和思考更广泛的自动化测试的策略。

    自动化测试的目的

    自动化测试的目的就是尝试并且确保你的软件干了你希望它干的事,不管是现在还是将来。

    这是个宽泛的定义,并且引出各种各样尝试和验证软件的方法:

    • 以已知的输入调用函数,并且断言期待中的结果
    • 构建测试网站并且验证网页,同时也检验了在网页之后的所有系统,可以正确执行简单的操作
    • 用大量随机输入看系统会不会挂掉
    • 将你的系统与另外的已知是好的参考实现对比,确保两者行为相同

    注意描述的目标没有提单元测试或集成测试。因为它们并不是最终目标:你希望用测试脚本自动检测你的软件是否按你所想的工作,以任何你觉得必要的方式。单元测试或集成测试与以各种方式进行的自动化测试仅有一个区别,我们后面会讨论这些方式中的一部分。

    现在我们定义了高层次的目标,在帖子余下部分将对细节进一步展开讨论。

    单元测试 vs 集成测试

    当你工作于自动化测试时,常会有一些相关讨论:

    • 我们正在写的是单元测试还是集成测试?
    • 我们应该写单元测试还是集成测试?
    • 如何定义单元测试或集成测试?

    有数不清的“真相只有一个”的区别来区分单元测试和集成测试,各不相同。类似于:

    • 单元测试必须始终在单个进程中运行
    • 单元测试不允许执行超过一个文件的代码:所有import必须被mock
    • 单元测试不能跨越服务器-客户端的边界

    然而,我认为这样的讨论常常缺少远见。在现实中,两者的精确边界经常是任意的。任何代码片段或系统总是集成了更小单元的单元:

    • 集群集成了多台物理或虚拟机器
    • 某台机器(虚拟或物理)集成了多个进程
    • 某个进程集成了多个子进程(如:DB,worker等)
    • 某个子进程集成了多个模块
    • 某个模块或包集成了多个更小的模块
    • 某个模块集成了单独的函数
    • 某个函数以基本算法集成了基本元素如Int等

    任何代码或系统片段可以被看成是一个单元进行测试,同时也可以被看成是更小单元的集成。基本上任何已经开发出来的软件都可以以下列层级方式进行分解:

                                _________
                               |         |
                               | Machine |
                               |_________|
                               /         \
                    _________ /           \ _________
                   |         |             |         |
                   | Process |             | Process |
                   |_________|             |_________|
                   /                       /         \
                  /              ________ /           \ ________
                ...             |        |             |        |
                                | Module |             | Module |
                                |________|             |________|
                                /        \                      \
                    __________ /          \ __________           \ __________
                   |          |            |          |           |          |
                   | Function |            | Function |           | Function |
                   |__________|            |__________|           |__________|
                   /          \            /          \           /          \
                  /            \          /            \         /            \
                ...            ...      ...            ...     ...            ...
    

    早先我们定义了自动化测试的目标是“尝试并且确保你的软件干了你希望它干的事”,并且在软件的任何部分,你都会在处于这个层级上所有等级的代码。所有这些代码当然都应该是要测试和验证的。

    为了让术语有连贯性,我将把对层级中处于较低级别代码(如,集成了基本元素的函数)的测试叫作单元测试,而把对于较高级别代码(如,集成了虚拟机的集群)的测试叫作集成测试。但是那边标签仅是简单是在一个范围中的方向,并不存在你可以应用在任意工程的,可以直接画在单元测试和集成测试之间的一条明显的界线。

                                大多数测试位于此之间
      单元测试 <--------------------------------------------------------> 集成测试
                  |           |           |            |          |
                 函数        模块        进程          机器       集群
    

    真正有关系的是你清醒的认识到你的软件划分为层级的方式,以及自动化测试可以位于代码层级的任意层次,在单元测试和集成测试这一区间的任意一点上。

    单元测试到集成测试之间是一个范围并不意味着它们之间的区别没有意义。虽然两种测试间有没明显界线,但向着两个区间端点靠近的测试有一些不同的属性:

    单元测试 集成测试
    位于较低层 位于较高层
    较快 较慢
    更可靠 更不可靠
    不怎么需要设置 需要一大堆设置
    依赖较少 依赖非常多
    出错方式较少 各种错误方式
    特定错误信息 广泛的错误信息
    • 单元测试趋于更快,因为它们只需要运行较少代码
    • 单元测试趋于更可靠,因为它们不太需要运行包含非确定错误的代码
    • 单元测试不太需要预先设置,办为它们依赖较少
    • 单元测试趋于发生相对特定的以较少的可能原因引发的错误(函数应返回1,但返回了2),而集成测试趋于更广泛的出错,由非常多种可能原因导致的各种无意义的错误(网页打不开)

    作为测试开发者,这对你意味着什么?

    单元测试和集成测试之间的区别由你来决定

    对于单元测试和集成测试,一套算法库与一个网站对其的定义是不同的,而一个网站与一套集群部署系统对其的定义也可能是不同的。

    • 算法库可能定义单元测试为以某些小数值作为输入运行一个函数(如,对0,1,2,3的数字列表进行排序),而集成测试是使用多个函数构造通用算法
    • 网站可能会定义单元测试为任何不涉及HTTP API的功能,而集成测试则是需要HTTP交互的那部分
    • 另一种选择是,网站可能定义单元测试为不引发浏览器切换(直到且包括API交互),而集成测试则是那些用Selenium通过UI/JavaScript与服务器的交互引发的浏览器切换
    • 集群部署系统则可能定义单元测试为任何不会物理上创建虚拟机的操作,直到且包括使用HTTP API或数据库访问的测试,而集成测试则是那些在测试环境中切换真实集群的操作

    尽管有一些区别(如,算法库的集成测试可能比集群部署系统的单元测试更快),所有这些系统都有其从“更单元”到“更集成”端的各自范围。

    因此,在它们之间画线取决于项目所有者,并且围绕此线展开实践。上面所述将给你一些关于在各种项目中如何画线的概念,并且围绕此线的实践可能看起来是这样的:

    • 单元测试必须在提交前运行,而集成测试每天只在每夜构建前运行一次
    • 与单元测试不同,集成测试运行于单独的CI服务器/集群,因为不同的设置需要

    它们可能在将测试区分为纹理清晰的分区时有些价值。再一次,它取决于项目拥有者,要画几条线,画在哪里,这些测试都叫啥(如,单元测试,集成测试,端到端测试,功能测试?),以及它们在项目中如何处理。

    在现今数量众多的各种软件项目中,并没有一个统一的关于单元测试和集成测试的分类,但并不意味着区分没有意义。只是简单的需要根据不同项目来画一条有意义且有用的线。

    每一层级的测试

    软件的任何部分都是以层级方式编写的,单元套着单元。在每一级别,程序员们都有可能犯错:理想情况下,我们的自动化测试能捕获的错误。

    因此,千万不要有类似规则:仅写单元测试,不写集成测试;只写集成测试,没有单元测试等等。

    • 不能只写单元测试。不管是所有函数都已被严格测试,但模块以不正确的方式组合了各函数,或者所有模块都已被严格测试,但应用进程以不正确的方式使用了模块。虽然一个测试套件能以极快的速度运行很棒,但如果它无法捕获程序上层的错误,则事实上并没什么用。
    • 不应该只写集成测试。理论上它能工作,在层次结构上层的代码调用与其相临层的代码,但你需要极大数量的集成测试以有效的运行低层代码。例如,当你想检测某个函数在10组不同基础数据参数情况下的行为,以集成测试的方式测试可能意味着你需要设置/释放整个应用进程10次:一种缓慢且严重浪费计算资源的行为。

    相反,你的测试结构应大致反映你的软件结构。你希望在所有层级上进行测试,某层上的代码量与出错概率/严重性基本成正比。这防止了在你软件片段的任何层级上引入错误的可能。

    如何对测试排优先级

    自动化测试服务于两个主要目的:确信你的代码没有破坏任何事(或许以某种手工测试难以捕获的方式),并且确保能正常工作的代码不会在将来某个时刻被破坏(回归)。前一种可能是由于未完成的实现,而后一种可能是因为随着时间推移,某次代码基线升级中的错误。

    因此,对于一些代码来说,自动化测试并不重要,比如看上去就不太会出错的代码,就算出错了也没什么关系的代码,或者某些除非有人特意搞挂掉都完全想不起来存在的代码。

    对于决定系统或代码片段需要怎样的测试程度,更像艺术而非科学,但还是会有一些准则:

    • 重要的事情需要更多测试!密码/鉴权肯定需要重点测试以确保坏密码不会让人以任何方式登录进系统,至少也要比应用的其它随机算法测的更多。
    • 不太重要的事情,只需要比较少的测试甚至不需要测试。或许你网站上的促销信息消失了几天,直到下次部署才重新出现,仅有的合适测试是通过昂贵的/缓慢的/不可靠的 Selenium集成测试。如果是这样,测试花费会指出,你或许并不需要为此做自动化测试。
    • 正在开发的代码需要更多测试,未在开发中的代码则只需要更少测试。如果某段可怕的代码片段几年没动过了,如果它以前没出过问题,那它就不太可能会出错。现在,你可能希望测试以确保它还没有被破坏,但你并不需要用测试来防止回归和出现新的错误。
    • 将要一直使用的API比可能被废弃的API需要更多测试。你应该聚焦于更有效的测试你应用中稳定的接口,而不是去测试可能在一周后完全消失的未稳定接口。结合前面的准则,稳定但内部正在集中开发的API需要更多的测试。
    • 如果代码的复杂性位于比较难测试的位置(进程间,浏览器-服务器,数据库交互),那不管多难测试,你都应该确保你测试了这个逻辑。造成不要只测简单的事:不管你对单个功能测试的多好,如果将它们连接起来的代码很脆弱,最终结果很可能就会出问题。

    所有这些观点都是主观的,并不能单从代码本身来判断。尽管如此,在对测试优先级排序时,你必须决定你开发自动化测试的精力该聚焦于何处。

    测试即代码

    测试脚本与其它的代码一样也是代码:测试套件是软件的一部分,其检测你的主软件以特定的方式运行。因此,你的测试代码应与其它任何软件片段一样被对待:

    • 公用测试逻辑应重构为助手(helper)。如果某个公用测试逻辑本身有BUG,那应该只需要修改一个地方,而非在整个套件中到处复制-粘贴以应用此修复。
    • 测试脚本应与普通代码一样有相同的代码质量标准:合适的命名,格式化,注释,在线文档,代码组织,代码风格和约定。
    • 测试脚本需要被重构以维护代码质量。任何代码在日益增长时都会变在混乱和难以维护,会需要重构以保持简捷无重复和良好组织。测试代码与此没有任何区别,当主应用的需求日益增长时,测试代码同样增长和变化以支持测试这些功能,它需要定期重构以保持无重复和良好的代码质量。
    • 测试套件应该足够灵活。如果要测试的API改变了,应可以迅速且容易的修改测试套件。如果某段代码被删掉,应该很放心的简单删除对应的测试代码,如果代码被重写,重写相应的测试代码不应该是件很困难的工作。适当的abstractions/helpers/fixtures能帮助确保修改或重写测试套件的某一部分不会是个繁重的负担。
    • 如果你的abstractions/helpers/fixtures开始变的复杂,其本身也应被测试,至少也应该最低程度被测试。

    并不是所有人都同意这些准则。我看到有人争论测试代码与普通代码并不相同。复制-粘贴测试代码并不仅仅是可接受,而是最好设置测试抽象和helper,以保持没有重复代码。争议部分在于只简单的看是否在没有抽象时测试代码是有没错误。我并不同意这个观点。

    我的观点是测试代码与其它代码一样,必须相同对待。

    DRY 数据驱动测试

    测试即是代码,代码应是无重复且只有必要的逻辑可见,不能有重复的样板。一个好的例子是定义测试助手以让你可以在你的测试套件中简单的堆放一堆测试用例,并且可以只简单的看一眼就知道你的测试套件所测试东西的输入。例如,有下面的测试代码:

    // 在交互解释环境中按下ENTER时的完整性检查,以检测一组输入是否...
    //
    // - 完整,无需额外输入即可提交
    // - 不完整,因此需要从用户获取更多输入行
    
    def test1 = {
      val res = ammonite.interp.Parsers.split("{}")
      assert(res.isDefined)
    }
    def test2 = {
      val res = ammonite.interp.Parsers.split("foo.bar")
      assert(res.isDefined)
    }
    def test3 = {
      val res = ammonite.interp.Parsers.split("foo.bar // 行注释")
      assert(res.isDefined)
    }
    def test4 = {
      val res = ammonite.interp.Parsers.split("foo.bar /* 块注释 */")
      assert(res.isDefined)
    }
    def test5 = {
      val res = ammonite.interp.Parsers.split(
        "val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0).sum"
      )
      assert(res.isDefined)
    }
    def test6 = {
      val res = ammonite.interp.Parsers.split("{")
      assert(res.isEmpty)
    }
    def test7 = {
      val res = ammonite.interp.Parsers.split("foo.bar /* 不完整的块注释")
      assert(res.isEmpty)
    }
    def test8 = {
      val res = ammonite.interp.Parsers.split(
        "val r = (1 until 1000.view.filter(n => n % 3 == 0 || n % 5 == 0)"
      )
      assert(res.isEmpty)
    }
    def test9 = {
      val res = ammonite.interp.Parsers.split(
        "val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0"
      )
      assert(res.isEmpty)
    }
    

    你能看到相同的事情被反复的干着。真应该被写成:

    // 在交互解释环境中按下ENTER时的完整性检查,以检测一组输入是否...
    //
    // - 完整,无需额外输入即可提交
    // - 不完整,因此需要从用户获取更多输入行
    
    def checkDefined(s: String) = {
      val res = ammonite.interp.Parsers.split(s)
      assert(res.isDefined)
    }
    def checkEmpty(s: String) = {
      val res = ammonite.interp.Parsers.split(s)
      assert(res.isEmpty)
    }
    def testDefined = {
      checkDefined("{}")
      checkDefined("foo.bar")
      checkDefined("foo.bar // 行注释")
      checkDefined("foo.bar /* 块注释 */")
      checkDefined("val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0).sum")
    }
    def testEmpty = {
      checkEmpty("{")
      checkEmpty("foo.bar /* 不完整的块注释")
      checkEmpty("val r = (1 until 1000.view.filter(n => n % 3 == 0 || n % 5 == 0)")
      checkEmpty("val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0")
    }
    

    这只是任何编程语言代码中该做的普通重构。尽管如此,这立刻将复制-粘贴繁重样板的测试方法转为灵活的,无重复的代码,并且只一眼就能看出正在测试什么输入,以及该有什么样的预期输出。也可以用其它方式来做,比如,定义所有Defined用例到数组中,所有Empty用例到另一个数组,并且循环以完成所有断言:

    def definedCases = Seq(
      "{}",
      "foo.bar",
      "foo.bar // line comment",
      "foo.bar /* block comment */",
      "val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0).sum"
    )
    
    for(s <- definedCases){
      val res = ammonite.interp.Parsers.split(s)
      assert(res.isDefined)
    }
    
    def emptyCases = Seq(
      "{",
      "foo.bar /* incomplete block comment",
      "val r = (1 until 1000.view.filter(n => n % 3 == 0 || n % 5 == 0)",
      "val r = (1 until 1000).view.filter(n => n % 3 == 0 || n % 5 == 0"
    )
    
    for(s <- emptyCases){
      val res = ammonite.interp.Parsers.split(s)
      assert(res.isEmpty)
    }
    

    两种重构方法完成同样的事情,并且有数不清的其它方法可以对代码去重。哪种方式完全取决于你自己。

    围绕于这个想法,有许多华丽的工具/术语:表驱动测试,数据驱动测试,等等。但从根本上来说,你想要的就是让你的测试用例简洁,并且期待/断言更尽于一眼可见。这是普通的代码重构技术可以帮助你完成而无需任何华丽工具的。只有当你尝试了手工重构,并且发现在某些方面的缺失,这时才值得开始寻找更特定的工具和技术。

    测试DSL

    有各种测试DSL,让你编写测试与普通代码时大相径庭。我发现通用的测试DSL一般并没什么用,不过另有一些专用于特定目的的特定DSL。

    通用测试DSL

    这些包括外部DSL,如Cucumber系列,提供了一种全新的编写测试的语法:

    Scenario: Eric wants to withdraw money from his bank account at an ATM
        Given Eric has a valid Credit or Debit card
        And his account balance is $100
        When he inserts his card
        And withdraws $45
        Then the ATM should return $45
        And his account balance is $55
    
    Scenario Outline: A user withdraws money from an ATM
        Given <Name> has a valid Credit or Debit card
        And their account balance is <OriginalBalance>
        When they insert their card
        And withdraw <WithdrawalAmount>
        Then the ATM should return <WithdrawalAmount>
        And their account balance is <NewBalance>
    
        Examples:
          | Name   | OriginalBalance | WithdrawalAmount | NewBalance |
          | Eric   | 100             | 45               | 55         |
          | Pranav | 100             | 40               | 60         |
          | Ed     | 1000            | 200              | 800        |
    

    而对于内部/内嵌的DSL,如Scalatest,能将宿主语言的语法转变为类似英语,以让你编写测试:

    "An empty Set" should "have size 0" in {
      assert(Set.empty.size == 0)
    }
    
    "A Set" can {
      "empty" should {
        "have size 0" in {
          assert(Set.empty.size == 0)
        }
        "produce NoSuchElementException when head is invoked" in {
          intercept[NoSuchElementException] {
            Set.empty.head
          }
        }
        "should be empty" ignore {
          assert(Set.empty.isEmpty)
        }
      }
    }
    
    val result = 8
    result should equal (3) // By default, calls left == right, except for arrays
    result should be (3)    // Calls left == right, except for arrays
    result should === (3)   // By default, calls left == right, except for arrays
    
    val one = 1
    one should be < 7       // works for any T when an implicit Ordered[T] exists
    one should be <= 7
    one should be >= 0
    
    result shouldEqual 3    // Alternate forms for equal and be
    result shouldBe 3       // that don't require parentheses
    

    我对于这样的DSL的观点是,它们通常不值得努力。它们增加了间接且复杂的层次,不管是通过特定如 Cucumber 那样的语法解释器,还是通过如 Scalatest 那样特定扩展方法和语法的方式。这两者都让我更难以指出测试正在测什么。

    我认为这样的语法不如仅用assert和普通的助手方法/for循环等,来编写测试。一般它们都会提供额外功能如比较好看的错误信息,然而现今的测试框架如PyTest和uTest同样可以用老的纯assert提供类似“好看”的错误信息。

    $ cat test_foo.py
    def test_simple():
        result = 8
        assert result == 3
    
    $ py.test test_foo.py
    =================================== FAILURES ===================================
    _________________________________ test_simple __________________________________
    
        def test_simple():
            result = 8
    >       assert result == 3
    E       assert 8 == 3
    
    test_foo.py:3: AssertionError
    =========================== 1 failed in 0.03 seconds ===========================
    

    正如先前所指出的,我认为测试即代码,并且因此开发普通代码时的编码工具,如函数,对象和抽象等同样可以很好的为编写测试服务。如果你并没在使用像 Cucumber 之类的外部DSL或 Scalatest 之类的内嵌类英语DSL来写你的主项目,那也不应该用类似的东东来写你的测试套件。

    专用测试DSL

    虽然我认为通用测试DSL如 Scalatest 或 Cucumber 并不是什么好语音,但专用测试DSL(如专用于定义测试用例输入/输出)还是有用处的。

    例如,MyPy 项目使用特定语法来定义测试用例的输入/输出来进行python类型检查:

    [case testNewSyntaxBasics]
    # flags: --python-version 3.6
    x: int
    x = 5
    y: int = 5
    
    a: str
    a = 5  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
    b: str = 5  # E: Incompatible types in assignment (expression has type "int", variable has type "str")
    
    zzz: int
    zzz: str  # E: Name 'zzz' already defined
    

    这里的 # E: 注释用于断言类型检查将在检查此文件时在特定位置抛出一个指定错误。

    我自己的 Ammonite 项目有其特定的语法用以断言在交互式解释环境会话中断言:

    @ val x = 1
    x: Int = 1
    
    @ /* trigger compiler crash */ trait Bar { super[Object].hashCode }
    error: java.lang.AssertionError: assertion failed
    
    @ 1 + x
    res1: Int = 2
    

    在这些例子中,DSL都被缩小了范围到其明显正在测试的东东。此外,这些DSL仅在当普通代码的方式有太多“杂音”时才是必须的。例如,上述Ammonite测试用例以普通代码方式定义时看起来是这样的:

    checker.run("val x = 1")
    checker.assertSuccess("x: Int = 1")
    
    checker.run("/* trigger compiler crash */ trait Bar { super[Object].hashCode }")
    checker.assertError("java.lang.AssertionError: assertion failed")
    
    checker.run("1 + x")
    checker.assertSuccess("res1: Int = 2")
    

    你能看到,Ammonite 的交互环境测试DSL对于普通代码而言,代码可读性得到明显提升!在这些情况下,DSL比普通代码能实际的减少了“杂音”,这时你就该用特定DSL来完成。在其它情况下,当然是默认情况下,你的测试应该以与其要测试的主代码基线一致的风格来编写。

    范例测试 vs 批量测试

    范例测试是指以单个(或少量)样例,测试整段代码,并且一路上仔细断言以确保代码做了正确的事情。批量测试,正好与之相反,是指以一批样例来测试代码,比较少对每个样例的行为详尽检查:只是确保它不会崩掉,以及或许粗略检查以确保其不是完全不正确的运行。Fuzz Testing 或 Property-based Testing 是这类测试的两种通用方式。

    就像单元测试和集成测试的区别一样,范例测试与批量测试也是个范围区间,大多数测试落于这个区间中的某个地方。例如,之前的DRY 数据驱动测试位于区间中:以相同规则检查超过一组数据,而非成百上千组不同的输入。

    范例测试-批量测试的范围区间与单元测试-集成测试的范围区间正交,你能轻松找到两个区间的每一极端例子:

    + 单元测试 集成测试
    范例测试 输入[1, 0]到排序算法并且确保变为[0, 1] 点击网站上的单一流程以确保该流程工作
    批量测试 输入大量的随机数到排序算法并确保最终被排序 整夜不停的随机点击网站并确保没有500错误出现

    范例测试

    范例测试经常是当人们听到自动化测试时首先想到的:测试以某种方式使用API,并且检测结果。这里有个来自我的FastParse库中的例子,用于测试一个parser在分析单个字符 a 的情况:

    import fastparse.all._
    val parseA = P( "a" )
    
    val Parsed.Success(value, successIndex) = parseA.parse("a")
    assert(
      value == (),
      successIndex == 1
    )
    
    val failure = parseA.parse("b").asInstanceOf[Parsed.Failure]
    assert(
      failure.lastParser == ("a": P0),
      failure.index == 0,
      failure.extra.traced.trace == """parseA:1:1 / "a":1:1 ..."b""""
    )
    

    你可以看到,有几个步骤:

    • 定义parser
    • 用它分析不同的字符串
    • 检测它在该成功或失败时成功或失败
    • 检测每个成功或失败的内容是我们所期望的

    这就像在REPL中乱逛一样,除了在REPL中,我们通常用眼睛关注库的返回值,而在这儿我们用assert。

    经常的,范例测试会与手工测试一起:在REPL中逛逛或运行开发的主方法以确保功能工作正常。然后你将做同样事情的测试添加到套件中以确保功能继续正常工作并且避免回归。如果你遵循测试驱动开发,你可能会先写测试,但其它的事情都是一样的。

    范例测试良好的文档化:仅仅是读些范例,就能相对清楚某模块干了什么以及它该被怎么样使用。范例测试对于覆盖期待中的成功或失败用例很管用。你可以用DRY数据驱动测试轻松覆盖一组测试用例的输入/输出,但最终你还是会被局限于你能想象出什么样的用例,其只是所有可能输入的一个子集。这时批量测试上场了。

    批量测试

    批量测试可以比你手工测试覆盖更多用例:代码并非只被运行一次然后检查,批量测试以不同的输入运行成百上千次。这让你可以覆盖你以手工方式未能想到的测试用例,或添加到范例测试中。

    有著名的方法进行批量测试,如Fuzz Testing 或 Property-based Testing,并且有框架如 QuickCheck 或 ScalaCheck 能帮助干这事儿,并且提供大量花哨的功能,但最终,批量测试能精简描述为下面类似的事情:

    for i in range(0, 9999):
        for j in range(0, 9999):
            result = func(i, j)
            assert(sanity_check(result))
    

    这里,我们以不同的输入调用 func 上亿次,以简单的 sanity_check 函数来检查,其并不知道所有输入的输出,但能检查基本的诸如“输出非负”。同时,我们检查 func 并未由于某些输入抛出异常或无限循环。

    何时要做批量测试

    由于测试输入数,批量测试要慢于单个用例的测试。因此它们的测试花费要远高,并且应被谨慎选用。尽管如此,当某个功能的输入范围极大且难以手工挑选测试用例来覆盖所有边界时,它们仍然是值得的。比如:

    • 数学算法会有大量 while 循环,如果实现不正确,则某些组合的输入极有可能导致无限循环
    • 编程语言分析工具,由于可能的输入程序有非常巨大的可能并且经常会包含一些不满足期望的样式
    • 日志文件分析工具,由于日志经常是混乱无结构的,很难知道何种模式该被接受或拒绝

    这种情况下,输入大量不同测试数据有助于找出你未曾注意到的边界用例。测试数据可以是大范围的随机数字,从互联网上找到的各种源代码,从你生产环境拉下来的一整天的日志。

    如何进行批量测试

    在进行大量输入集合的时候,“正确”并不是由同样大量的一组预期输出。相反,“正确”通常都定义为输入输出间的相对关系是否如预期般的正确,而并不去管输入究竟是什么:

    • 任何输入不应导致程序抛出异常或无限循环
    • 排序函数的输出必须包含所有的输入列表中的值正好一次,输出列表必须已排序
    • 从样例日志处理过的所有行必须在X和Y之间包括一个日期,该日志的其它行必须没有标识 USER_CREATED

    与范例测试相比,对批量测试的检查通常会比较简单和没那么精确。一般不会有人在处理很大的日志文件后,对返回结果与某个几千行的预期值列表进行精确断言:很可能在预期结果中也会不小心搞个错误,而你真正需要关注的是处理器本身。然而,不论程序的输出的准确值到底是什么,我们知道一些属性应该永远为true。那些属性才是批量测试时该真正验证的。

    除了用for循环生成一堆输入数据之外,你也可以找到大量真实世界的输入数据喂给你的代码。假设如果我们要测试的程序要处理 Python 源代码,这样的批量测试可能看起来是这样的:

    repos = [
        "dropbox/changes",
        "django/django",
        "mitsuhiko/flask",
        "zulip/zulip",
        "ansible/ansible",
        "kennethresitz/requests"
    ]
    for repo in repos:
        clone_repo("https://github.com/" + repo)
        for file in os.walk(repo):
            if file.endswith(".py"):  
                result = process_python_source(file)
                assert(sanity_check(result))
    

    批量测试通常会比范例测试慢很多:或许几秒或几分钟,而不是多少毫秒。此外批量测试趋于难读难懂:当你生成几千组测试数据或从互联网加载几千组测试输入时,很难判断哪些输入是连界用例,哪些输入只是普通不需要太关注的。

    缩减批量测试至范例测试

    因此,经常值得缩减批量测试将那些引发BUG的用例并添加到范例测试套件中。这意味着你的范例测试最终将包含在批量测试中出现的很好的边界用例。这也能成为很好的边界用例的文档,以让某位在将来修改代码的人引起注意,并让他们可以在毫秒级的时间里迅速的测试某些最重要的边界用例。

    我的 FastParse 库有一套这种风格的测试套件:以一套可扩充的批量测试花费N分钟从互联网下载数以千计的源代码,处理分析,并执行基本检查(所有已有的处理工具可以成功处理的文件,我们也应该可以处理)。这伴随着大量DRY数据驱动的范例测试。这些范例中包括了所有曾在批量测试中发现过的BUG范例,并可以在不到1秒内运行完成。

    再说一遍,有很多基于属性的测试工具,如 QuickCheck 或 ScalaCheck 能帮助编写这类批量测试。它们让生成大批量典型数据以喂到你的程序变得很容易,并自动找到少量引发失败的输入,以便更易于调试,以及其它很多优秀的功能。然而,它们并不是必须的:一些时候,几个for循环,一堆生产环境数据,或几个从“野外”找到的大型输入数据就能干到这个。如果你发现缺少快速生成有效数据的方法时,才应该去找更精密的工具。

    测试花费

    测试并非无花费的:毕竟,总得有人去写测试脚本!即使已经写完了,测试仍然并非无花费!每个测试都会增加测试套件的花费。每个测试:

    • 让测试套件变慢
    • 让测试套件更不可靠
    • 需要维护:当主代码改变时更新,在重构时检查,等

    这些并非理论上的考虑:

    • 我在代码基线上开发,每天运行几百次遍上万个测试:即使每个测试有百万分之一的可能的不可靠,那每天也会出个几次错误了,这样的错误会让工程师迷惑和沮丧。运行测试每月几十万次,并且我们维护的上百万行代码都会把我们拖慢。
    • 在我的开源工作中,每个项目都需要大约半个小时来运行CI中的测试(在5个并行进程的情况下!),并且由于不可靠引起的编译错误让我非常沮丧。

    在多台机器上并发运行测试可以加速缓慢的测试,但是需要¥¥¥,每台机器的设置开销远超只在一台机器上运行测试。

    自动测试在一些情况下会变得毫无价值:必须持续不停的运行,不可靠,很难维护且/或去覆盖低测试优先级的内容。这些测试实际上是有害的:它们就不该被开发,并且如果已被开发,就该被删掉。我个人就删了很多类似测试,比如,一个网站促销体验的 selenium 测试:

    • 给测试套件增加了15分钟时间:每个 selenium 测试都能轻松增加一分钟,而这个测试大部分通过 selenium
    • 每天都要出错几次
    • 要测试的功能如果不正确应该立刻就被注意到了:用户体验应该已经由AB测试所记录并追踪用户反馈
    • 哪怕真的挂掉一天也没什么实质损失:没有数据丢失,没有功能受损,甚至不会有用户注意到
    • 哪怕真的很重要,在体验被废弃前的2-3周内也无法捕获任何BUG或回归

    这种情况下,你该感谢开发者已经尽最大可能成为优秀工程师并测试了他们的代码,但若无法提升权重,无论如何直接删除掉这些测试。

    在我的开源工作如 Ammonite 中,我类似的最终从我的测试套件中删除了许多测试,这些测试增加了几十分钟的运行时间却没办法捕获测试矩阵中其它测试无法捕获的BUG。虽然在每个 Scala 版本和每个 JVM 版本的生产版本上运行测试很好,但如果它花费太多时间只能捕获非常少的BUG,那在实践中就没有什么价值。

    重构以减少测试花费

    除了不写或删除花费太高的测试,你也可以将精力放到减少已有测试的花费上。例如,重构/模块化代码通常会将你的测试代码从大型的集成测试推向小型化的单元测试,其更快更可靠:

    • 全局变量常常迫使你要启动新的子进程去测试应用逻辑。如果你重构逻辑以去掉对全局变量的依赖,你可以在同一个进程中测试这个逻辑,这通常会更快。这对于启动缓慢的平台如JVM特别重要,但哪怕快捷的解释器如 Python 在运行前加载模块也能轻松用去10到100毫秒。
    • 数据库访问比内存逻辑更慢;例如,在你代码中从头到尾的都要从数据库加载一些数据,不如先加载必须的数据然后吐到核心业务逻辑中。这样你可以只靠内存运行你的核心业务逻辑的测试而无需数据库,这远远快于它们必须时刻与数据库交互。

    本质上来说,这将会进化你的看起来这样的整体应用:

                         ____________________
                        |                    |
                        |                    |
                        |        应用        | <-- 无数集成测试
                        |                    |
                        |____________________|
    

    并将其拆分为看起来这样的一堆:

                             __________
                            |          |
                            |   主模块  |  <------- 少量集成测试
                            |__________|
                            /   |  |   \
                 __________/    |  |    \__________
                /               /  \               \
     __________/     __________/    \__________     \__________
    |          |    |          |    |          |    |          |
    |   模块   |    |   模块    |    |   模块   |    |   模块    | <-- 大量单元测试
    |__________|    |__________|    |__________|    |__________|
    

    现在原先的庞然大物变成了更小的单元,你可以从测试区间的集成测试端迁移到单元测试端:这么多之前测试庞大应用的集成测试现在可以迁移到对单独模块的单元测试,可以参考之前的“每一层级的测试”。

    这种情况下,你通常会只留少量的集成测试以运行主模块以检测整体流程,并确保不同的模块能正确的在一起工作。这样,将庞大应用拆分为模块,并更新测试以适合这种模式,可以让你的测试套件运行的远快于之前并且更可靠,而并不会损失很多BUG捕获点。

    再提醒一遍,这个策略应该应用于你代码的每一个层级,不管你是拆分一个庞大的集群,庞大的应用进程,或是一个庞大的模块。

    如果你的测试套件变得很大/很慢/不可靠,并且你不愿意删除测试或砸钱让它们并发运行于多台机器,那么尝试重构代码发将集成测试转为单元测试是条很好的路子。

    写出负面价值的测试令人惊讶的轻松。测试都有不断投入的花费:运行时,不稳定,和维护它们。开发者们绝对应该记住这点,并且经常使用,以最大化编写和维护自动化测试套件的投入产出比。

    结论

    此文章总结了我在写自动化测试时的考虑因素:

    此文章的目的是描绘出与普通讨论不同的关于自动化测试的画面:自动化测试位于连续的测试区间中,而非不连续的某个点上,并且完全取决于项目拥有者来组织它们。由于测试即是代码,也应有同样的约束并可以用相同的技术,而非被当成特殊而不同的东东。由于测试有优先级,那些其提供的价值低于其不断的花费的测试就应被剔除。

    此文章特意淡化了测试相关的热门话题:测试驱动开发,代码覆盖率,UI测试,以及其它很多东西。在使用特定工具和应用特定技术之外,此文章旨在描绘如何思考任意软件项目自动化测试的准则。

    即使没有这些指导,希望此文章也能给你提供一个基础,能帮你制定,讨论,评估任意关于自动化测试的工具,技术,或实践,而不用管你正在工作于何项目。

    展开全文
  • 自动化测试的基本原则

    千次阅读 2018-08-13 15:03:26
     每个实行持续交付的项目,都有生产流水线的元素,如持续集成和自动化测试。这些测试是在不同层面进行的,从单元测试到冒烟测试再到功能测试。自动化功能测试的优点之一是可重复性和可预测的执行时间。出于这个原因...

    介绍
      每个实行持续交付的项目,都有生产流水线的元素,如持续集成和自动化测试。这些测试是在不同层面进行的,从单元测试到冒烟测试再到功能测试。自动化功能测试的优点之一是可重复性和可预测的执行时间。出于这个原因,它应该作为软件质量的每一个构建之后的指标。功能测试自动化往往会成为一个瓶颈,所以你应该熟悉一下如何创建这样的测试的基本原则。
      
      首先设计你的测试
      测试集合可以比作盆景树。
      最初的时候,我们照顾树根和树干。我们选择会成长的主要分支,我们每天都细心照料这棵树并等待它长出健康的叶子。
      我们可以以类似的方式继续测试。
      我们建一个将负责应用程序主要功能(例如:开启)的基类。
      根据说明,我们先明确将被测试覆盖的应用程序的主要功能,然后每天我们在执行测试的时候都添加更多平行测试。
      每一个支持测试(例如创建一个新的用户)的方法都需要与测试分离——让我们在单独的类里面来实现。
      你应该在包括了应用程序主要功能的目录里保持类。
      去建一个规定很多功能共有方法的抽象类是很好的做法。
      如果你正在测试Web应用程序,就用页面对象设计模式。该模式里,一个类及其方法对应了单个页面的功能或一个大型网页里单个页面上的一个元素。
      
      
      无需事事自动化
      自动化花费很多,所以你应该主要测试应用程序的主要功能。
      某些测试可以快速轻松地手动完成,且潜在脚本可能难以实现。
      值得用到自动化的是那些繁琐的需要被重复很多次的,和那些需要大量数据验证的测试工作。
      
      写短测试
      在一个或多个测试失败的情况下,开发团队的任何成员都应该能够轻松地找到错误的原因。
      出于这个原因,每个测试方法里应该最多有5个断言,并且每个方法都必须提供的测试操作的完整记录。
      明智的做法是使用BDD(行为驱动开发)技术,但是当你没有用一个特定的测试框架时,你应该把接下来的测试步骤放在comments //given //when //then下。
      
      创建独立测试
      在测试类中的每个方法应该是一个独立的实体,而不是依赖于其他测试。我们应该能够在任何时间运行单个测试。否则,这样的测试用例集将来维护起来就会很贵——必须定期跟踪和更新测试之间的联系。
      很多时候,测试需要一定的前提条件来满足。这些条件不应该用外部方法,应该在试验开始时运行。如果这些条件和测试类的所有方法一样,它们就可以被放在一开始进行的方法里(例如:在JUnit中被标记为@ BeforeClass)。
       
      关注可读性
      源代码应该是自我记录的,而写下以下几行代码的每个利益相关者应该明白测试在做什么,为什么它被这么写。尽量避免在源代码注释,因为它也必须被更新。这值得花比平常多一点的时间来命名方法,从而使你的代码更易读。
      再看看行为驱动开发技术,每个测试方法都应以单词“应该”开始,而不是“测试”。
      根据这一惯例,我们马上就可以明白一个特定的方法测试什么内容了,它在分析测试报告时特别有用。
      
      测试必须要快
      正如在本文开头所提到的,自动化功能测试应该是应用程序质量的一个指标。连续传递过程中的每一步都应指明最长持续时间;并且根据这个概念,开发团队应该尽快获得有关软件质量的反馈。自动化功能测试的持续时间应不超过几分钟。
      对一个非常全面的测试集来说,有必要并行运行测试(经常在不同的机器上)。虚拟化在这里可能是非常有帮助的。
      
      创建抗变测试
      最常提及自动化功能测试的缺点是其对应用程序中变化的低抵抗性,尤其是在GUI中。
      在Web应用程序中,测试应该能抵抗网站的内容的变化。测试应该只验证功能,这就使得它可以在不同的位置运行测试。这并不意味着我们不应该编写自动化测试来检查网页的内容。
       如果你已经想创建这样的测试,你应该遵循DDT(数据驱动测试)技术。这意味着,检查内容是与源代码分离开的。
      Web应用程序的页面布局变化非常频繁,这已经影响到了用户界面。
      当你设计一个界面时,每个区段和每个页面你都应该有一个你可以用来测试的唯一标识符,即使一个网站的层次结构发生了变化。
      
      自动化测试无法取代人类
      功能自动化测试可以是项目中的主要测试技术,但绝不是唯一的一个。
      自动化测试是可重复再生的,他们的覆盖范围总是相同的。
      另一方面,虽然探索性测试是低再生的,但是它们能够覆盖自动化测试未触及的区域。
      你还应该记住,自动化测试的“绿色”状态并不意味着你的应用程序是没有错误的。
      这种情况往往会让测试员分心,而且有可能会影响测试的准确性。

    展开全文
  • 自动化测试用例设计原则(转)

    千次阅读 2018-06-03 09:28:47
    一、自动化测试存在的真正意义: 主要用来保证产品主体功能正确完整和让测试人员从繁琐重复的工作中解脱出来。它的主要目的在于验证问题,而不是发现问题。所以对于自动化的设计,主要集中在功能正确性方面。 目前...

    内容摘自:http://www.cnblogs.com/jshtest/p/6362677.html

    一、自动化测试存在的真正意义:

    主要用来保证产品主体功能正确完整和让测试人员从繁琐重复的工作中解脱出来。它的主要目的在于验证问题,而不是发现问题。所以对于自动化的设计,主要集中在功能正确性方面。

    目前自动化测试阶段定位在冒烟测试和回归测试。冒烟测试执行的是主体功能点的用例,回归测试执行全部或部分的测试用例。

    二、自动化测试用例的设计原则:

    1、一个脚本是一个完整的场景,从用户登陆操作到用户退出系统关闭浏览器。

    2、一个脚本只验证一个功能点,不要试图用户登陆系统后把所有的功能都进行验证再退出系统

    3、尽量只做功能中正向逻辑的验证,不要考虑太多逆向逻辑的验证,逆向逻辑的情况很多(例如错误的登录账号有很多情况),验证一方面比较复杂,需要编写大量的脚本,另一方面自动化脚本本身比较脆弱,很多非正常的逻辑的验证能力不强。(我们尽量遵循用户正常使用原则编写脚本即可)

    4、脚本之间不要产生关联性,也就是说编写的每一个脚本都是独立的,不能依赖或影响其他脚本。
    5、如果对数据进行了修改,需要对数据进行还原。

    6、在整个脚本中只对验证点进行验证,不要对整个脚本每一步都做验证。

    三、用例选择注意事项:

    1、不是所有的手工用例都要转为自动化测试用例。

    2、考虑到脚本开发的成本,不要选择流程太复杂的用例。

    3、选择的用例最好可以构建成场景。例如一个功能模块,分n 个用例,这n 个用例使用同一个场景。这样的好处在于方便构建关键字测试模型。
    4、选择的用例可以带有目的性,例如这部分用例是用例做冒烟测试,那部分是回归测试等,当然,会存在重叠的关系。
    5、选取的用例可以是你认为是重复执行,很繁琐的部分,例如字段验证,提示信息验证这类。这部分适用回归测试。

    6、选取的用例可以是主体流程,这部分适用冒烟测试。

    7、测试用例需要更多的关注功能逻辑的实现,而不必纠结某些字段的限制。

    8、自动化测试也可以用来做配置检查,数据库检查。这些可能超越了手工用例,但是也算用例拓展的一部分。项目负责人可以有选择地增加。

    9、如果平时在手工测试时,需要构造一些复杂数据,或重复一些简单机械式动作,可以让自动化脚本来帮你。

    四、自动化测试用例转型原则

    1、当前的测试用例前置配置信息要写清楚。

    2、每一个步骤都要衔接好,错了,脚本要抛出异常。

    3、每一个步骤要做什么,验证什么要写清楚,写具体。有时一个检查点,你只需看一眼,但是脚本要写一堆代码去验证,这样的做法是不可行的。

    4、用例之间不要有关联性,自动化测试开发同样是软件开发工程,脚本编写同样提倡高内聚低耦合的理念。

    5、不是每一个步骤都需要验证点。

    6、别在多个地方重复相同的验证。脚本很忙!我没空。当然,除非有必要。

    7、开门记得要关门,配置信息要回归原点,否则脚本要迷路。

    展开全文
  • 1.自动化测试的含义 自动测试就是用程序代替人的手工操作,完成一系列测试的过程。 √     自动化工具能自动打开程序、自动执行测试用例、自动查找控件、自动产生数据、自动输入数据、...

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

    一、理解软件测试自动化

    1.自动化测试的含义

    • 自动测试就是用程序代替人的手工操作,完成一系列测试的过程。
      √     自动化工具能自动打开程序、自动执行测试用例、自动查找控件、自动产生数据、自动输入数据、自动操作控件、自动收集结果、自动比较实际结果与预期结果是否一致。

    2.软件测试为什么要自动化?

    在这里插入图片描述

    • 软件测试是一项工作量巨大的工作;

    • 软件测试包含大量的重复性操作;

    • 软件测试的某些环节包含一些非智力创造性活动;

    • 很多情况下手工测试难以模拟真实的环境;

    • 手工测试无法提供精确的测试结果。

    3.自动化测试的优点

    • 自动化测试可重复执行,能执行更多、更频繁的测试。

    • 能执行一些手动测试比较困难或不可能进行的测试。

    • 能更好地利用资源,可利用晚上或周末空闲的设备执行自动化测试。

    • 自动化让测试人员腾出时间和精力,测试人员可以投入更多的精力设计出更多、更好的测试用例,从而提高测试准确性和测试人员的积极性。

    • 自动测试具有一致性的特点,能够保证测试更客观,从而提高了软件的信任度。

    4.自动化测试的缺点

    • 不能完全代替人工测试,不是所有的测试用例都可以自动化,工具本身不具备思维能力。
      √     设计用例。
      √     界面和用户体验测试。
      √     正确性检查。

    • 不能保证 100%的测试覆盖率。

    • 需要更长的时间去分析和隔离所发现的缺陷。

    • 自动化测试对软件质量依赖性较大。

    • 如果测试人员不熟悉某些测试工具,测试工作的进度就有可能受到影响。

    • 不能立即降低测试投入,提高测试效率。自动化测试的成本问题可能高于人工测试,因为工具的购买及维护的开支很大。

    5.自动化测试应用场合

    在这里插入图片描述

    6.不正确的自动化测试期望

    • 有了工具,一切测试过程都变得自动了。
      √     如果项目中使用了很多第三方控件或自定义控件,而这些控件的可测性很差,这种测试则不适合自动化。

    • 有了工具,测试工作马上就减轻了。
      √     购买测试工具后,还需要编写和维护测试脚本,这些费时、费力;
      √     可以在界面雏形时期,检查界面中的控件是否可测,从而选择合适的工具。

    • 自动测试工具都是简单易用的。
      √     功能越完备操作通常越复杂,要求使用者掌握更多的技能。

    • 自动化测试尽早执行。
      √     自动化测试需要过早计划但不宜过早执行;自动化测试需要循序渐进进行。

    二、常用的自动测试工具及分类

    1.商业测试工具

    • 需要购买,价格昂贵。

    • 成熟、稳定、有售后服务和技术支持。

    • 适宜 GUI 功能和性能测试。

    • HP
      √     QuickTest Professional/UFT(Unified Funtional Testing):功能测试工具。
      √     LoadRunner:性能测试工具。
      √     Quality Center/Application Lifecycle Management:测试管理工具。

    • IBM Rational
      √     Robot:功能和性能测试工具。
             ✰     支持 HTML、Java、.Net、Visual Basic、PowerBuilder、Delphi、Oracle 表单和 MFC 控件。
      √     ClearCase:软件配置管理工具。
      √     ClearQuest:缺陷和变更跟踪工具。
      √     TestManager:测试管理工具。

    • Compuware QACenter
      √     QARun:功能测试工具。
      √     QALoad:性能测试工具。
      √     QADirector:测试管理工具。

    • Microsoft
      √     Web Application Stress:性能测试工具。

    2.开源测试软件

    • 志愿者开发和维护,未必完全免费。

    • Selenium
      √     最早由 Thoughtworks(思特沃克)的员工 Jason Huggins 编写,后来多人加入。
      √     功能和兼容性自动化测试工具。

    • Jmeter
      √     Apache 组织开发。
      √     性能和接口自动化测试工具。

    三、自动化测试工具的选择

    • 选择测试工具的指导原则
      √     一般不是在项目初期完成工具选择,往往是在开发工具确定很长时间以后才能完成,甚至是项目后期才明确。
      √     选择适合自己公司项目的产品,只买对的,不买贵的。
             ✰     不要轻信测试销售人员的介绍就轻易购买,一定要组织详细的试用,确认适合项目使用,才能购买。
      √     分阶段、逐步引入测试工具。
      √     选择技术支持完善的产品。
      √     尽量选择主流的测试工具。
      √     如需多种工具,尽量选择一个公司的产品。
      √     考虑测试工具的集成能力(操作系统、开发工具、其他测试工具)。需要考虑:与开发语言一致的测试脚本语言,还要注意第三方控件与脚本语言能否匹配。
             ✰     测试工具未必支持所有的控件
                     ▲     可能会出现不识别的问题,不能花太多时间去研究为什么不识别,应该先用最简单的方法解决,使自动化测试得以进行是最重要的。
                     ▲     遇到不能识别控件的问题时,可以向开发寻求帮助,让开发提供对软件的编程接口,更换一个同等效果的工具等。
      √     测试用例的自动化应该注意顺序
             ✰     先自动化简单的、主要功能的用例,然后向次要功能等扩展。
    展开全文
  • selenium自动化测试实战

    万次阅读 多人点赞 2018-01-13 21:19:08
    一句话,自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。 Selenium 2,又名 ...
  • 自动化测试的五大原则

    千次阅读 2015-04-26 14:49:06
    1.自动化测试用例范围往往是核心业务、流程或者重复执行率较高的。 2.自动化测试用例的选择一般以“正向”为主。 3.不是所有手工用例都可以使用自动化测试来执行。 4.手工测试用例往往不需要回归原点,而自动化...
  • 今天总结一下在做自动化测试中测试用例设计的一些建议,总结为三原则: 1. 保持Case之间的独立性 case独立性就是能够独立运行,当我们有随机的跑其中某个Case或乱序的跑这些Cases时,测试的结果都应该是准确的。 ...
  • 自动化测试框架

    千次阅读 2018-05-09 15:00:15
    什么是自动化测试框架 自动化测试框架是应用于自动化测试的程序框架,它提供了可重用的自动化测试模块,提供最基础的自动化测试功能,或提供自动化测试执行和管理功能的架构模块。它是由一个或多个自动化测试基础...
  • QTP-自动化测试用例设计原则

    千次阅读 2012-06-18 20:48:54
    原则1:自动化测试用例的范围往往是核心业务流程或者重复执行率较高的。 如果项目的变更频率,测试用例数量大的话,增加了后期的维护工作量等,都是造成最终失败的一些隐患。投入越大,损失越大。因此,往往我们...
  • 使用脚本设计自动化测试框架的原则   前言:   使用脚本语言来设计自动化测试开发框架,是很多大型IT企业在进行自动化测试中所采用的方法,一个好的自动化测试框架,可以大幅度提高测试人员自动化脚本开发的...
  • 自动化测试介绍 自动化测试(Automated Testing),是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架,编写自动化测试用例,来模拟手工测试过程。比如说,在项目迭代...
  • 合理使用以下指导原则,可以有助于自动化测试工作的开展。 指导原则1:避免过早开发测试脚本 我们鼓励软件测试人员,在应用程序开发初期就开展自动化测试。但是,在程序功能还不齐全的情况下,编写自动化测试...
  • IOS 自动化测试

    千次阅读 2020-03-25 16:48:49
    自动化测试节省时间节省真机的成本,而且更高效的覆盖所有的iOS机型测试,避免每次上线前重复的人工回归测试,保证每次上线的版本稳定运行。
  • 设计自动化测试用例的原则

    千次阅读 2015-08-08 14:49:32
    这是再基本不过的要求了,但别看只是简单的一句话,要能够达到切实覆盖全面,需要对被测试产品功能的全面了解、明确测试范围(特别是要明确哪些是不需要测试的)、具备基本的测试技术(如:等价类划分等)等。...
  • 自动化测试用例设计

    千次阅读 2018-09-19 16:22:09
    一、了解自动化测试的目的和作用  自动化测试是为了让测试人员从繁琐重复的机械式测试过程中解脱出来,把时间和精力投入到更有价值的地方,从而挖掘更多的产品缺陷。目前自动化测试更多的是定位在冒烟测试和回归...
  • 前端自动化测试实践

    万次阅读 多人点赞 2018-10-31 23:35:49
    通过前端自动化测试,来解放自我

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 154,475
精华内容 61,790
关键字:

自动化测试原则