zoukankan      html  css  js  c++  java
  • cucumber java从入门到精通(4)Scenario Outline及数据驱动

    cucumber java从入门到精通(4)Scenario Outline及数据驱动

    到目前为止,我们的TodoList类工作良好,不过离我们的预期——任务清单系统还是有不少差距,究其原因不过如下:

    • 我们的feature不太完毕,没有测试任务清单的增删改查完成等功能;
    • 我们输入的数据太过单一,只测试了1种输入输出的情况;

    下面我们将着手解决数据输入太过单一的问题。我们将使用Scenario Outline技术。

    什么是Scenario Outline

    什么是Scenario Outline呢,我们不妨先去命令行里看一下,在命令行中输入

    java -cp "jars/*" cucumber.api.cli.Main --i18n zh-CN
    

    这将得到cucumber关键字的翻译:

    | feature          | "功能"                   |
    | background       | "背景"                   |
    | scenario         | "场景", "剧本"             |
    | scenario_outline | "场景大纲", "剧本大纲"         |
    | examples         | "例子"                   |
    | given            | "* ", "假如", "假设", "假定" |
    | when             | "* ", "当"              |
    | then             | "* ", "那么"             |
    | and              | "* ", "而且", "并且", "同时" |
    | but              | "* ", "但是"             |
    | given (code)     | "假如", "假设", "假定"       |
    | when (code)      | "当"                    |
    | then (code)      | "那么"                   |
    | and (code)       | "而且", "并且", "同时"       |
    | but (code)       | "但是"                   |
    

    我们可以看到scenario outline被翻译成了场景大纲或者是剧本大纲。简单来说,场景大纲可以理解为同一个场景同一部戏,内容都一样,台词也一样,只是换了不同的演员来演。每个场景大纲定义了不同的演员列表,该场景的台词是一样,演员的动作也相同,只是每次换不同的演员去表演。所谓铁打的营盘流水的兵。因此,场景大纲里定义了几组演员,这个场景就要演几次。

    cucumber的场景大纲不是用来定义演员的,而是用来定义数据的。大纲里定义几组数据,那么该场景就要跑几次。

    增加Example

    现在我们给我们的feature增加1组测试数据:

    #language: zh-CN
    
    功能:任务管理
    
        场景大纲: 完成任务
            假设 我的任务清单里有<total>个任务
            当 我完成<finished>件任务之后
            那么 我还剩下<left>件未完成的任务
            
            例子:
            | total | finished | left |
            |   3   |    1     |   2  |
            |   5   |    1     |   4  |
    

    运行一下

    run
    

    我们发现cucumber报错了:

    #language: zh-CN
    功能: 任务管理
    
      场景大纲: 完成任务             # todo.feature:5
        假设我的任务清单里有<total>个任务
        当我完成<finished>件任务之后
        那么我还剩下<left>件未完成的任务
    
        例子:
    
      场景大纲: 完成任务       # todo.feature:12
        假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)
        那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int)
    
      场景大纲: 完成任务       # todo.feature:13
        假设我的任务清单里有5个任务 # TodoStep.iHaveSomeTasks(int)
          java.lang.AssertionError: expected:<3> but was:<5>
            at org.junit.Assert.fail(Assert.java:88)
            at org.junit.Assert.failNotEquals(Assert.java:834)
            at org.junit.Assert.assertEquals(Assert.java:645)
            at org.junit.Assert.assertEquals(Assert.java:631)
            at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
            at ?.假设我的任务清单里有5个任务(todo.feature:6)
    
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)
        那么我还剩下4件未完成的任务 # TodoStep.iLeftSomeTasks(int)
    
    Failed scenarios:
    todo.feature:13 # 场景大纲: 完成任务
    
    2 Scenarios (1 failed, 1 passed)
    6 Steps (1 failed, 2 skipped, 3 passed)
    0m0.166s
    
    java.lang.AssertionError: expected:<3> but was:<5>
            at org.junit.Assert.fail(Assert.java:88)
            at org.junit.Assert.failNotEquals(Assert.java:834)
            at org.junit.Assert.assertEquals(Assert.java:645)
            at org.junit.Assert.assertEquals(Assert.java:631)
            at step_definitions.TodoStep.iHaveSomeTasks(TodoStep.java:15)
            at ?.假设我的任务清单里有5个任务(todo.feature:6)
    

    看起来密密麻麻,实际上的意思就是场景大纲里的第二组数据报错了,我们的TodoList的实现无法满足第二组数据,想想这也是应该的。

    在这里要解释一下例子这个关键字。例子在英文里叫Example,是feature关键字。紧跟在例子后的一半都是1个数据列表,从上文可以看到,一目了然。数据列表有表头,表后下面的行跟着的是测试数据。

    Scenario Outline运行流程

    Scenario Outline的运行流程就是先从例子里读一行数据,然后根据该数据的column也即是表头,在steps里找到相应的<column>字段,用具体的数据进行替换。例子里有2行数据这个Scenario就会运行2次。因此我们上面的feature文件实际上是这样运行的:

    假设 我的任务清单里有3个任务
    当 我完成1件任务之后
    那么 我还剩下2件未完成的任务
    
    假设 我的任务清单里有5个任务
    当 我完成1件任务之后
    那么 我还剩下4件未完成的任务
            
    

    重构并让用例通过

    我们的用例已经测试出TodoList类的缺陷了,是时候重构一下了,先重构TodoStep.java文件

    // TodoStep.java
    package step_definitions;
    
    import cucumber.api.java.zh_cn.*;
    import cucumber.api.PendingException;
    import static org.junit.Assert.*;
    import implementation.TodoList;
    
    public class TodoStep {              
        TodoList todo;
        
        @假设("^我的任务清单里有(\d+)个任务$")                                                   
        public void iHaveSomeTasks(int totalTasks) throws Throwable {                        
            // Write code here that turns the phrase above into concrete actions     
            todo = new TodoList();
            todo.setTotalTaskCount(totalTasks);
        }                                                                            
                                                                                     
        @当("^我完成(\d+)件任务之后$")                                                       
        public void iFinishSomeTasks(int finishedTasks) throws Throwable {                           
            // Write code here that turns the phrase above into concrete actions     
            todo.finishTask(finishedTasks);                                            
        }                                                                            
                                                                                     
        @那么("^我还剩下(\d+)件未完成的任务$")                                                   
        public void iLeftSomeTasks(int leftTasks) throws Throwable {                        
            // Write code here that turns the phrase above into concrete actions        
            assertEquals(todo.getRestTasksCount(), leftTasks);                                            
        }                                                                            
    }                                                           
    

    我们这里假设TodoList有1个setTotalTaskCount方法,该方法用来设置当前TodoList中task的总数。另外我们还把假设中的断言给去掉了,这是因为如果假设中有断言,那就意味着你连前置条件都不信任,既然前提条件都不能保证,那么下面的步骤就没有太多意义了,所以去掉会让步骤定义更加的符合逻辑。

    再重构TodoList.java文件

    package implementation;
    
    public class TodoList {
        int totalTaskCount;
        int finishedTaskCount;
        
        public TodoList() {
            totalTaskCount = finishedTaskCount = 0;
        }
        
        public int getTotalTaskCount() {
            return totalTaskCount;
        }
        
        public void setTotalTaskCount(int count) {
            totalTaskCount = count;
        }
        
        public void finishTask(int count) {
            finishedTaskCount = count;
        }
        
        public int getRestTasksCount() {
            return totalTaskCount - finishedTaskCount;
        }
    }
    
    

    代码很简单,就不一一解释了。

    运行一下

    compile && run
    

    结果如下,所有的step都pass了。

    #language: zh-CN                                                          
    功能: 任务管理                                                                  
                                                                              
      场景大纲: 完成任务             # todo.feature:5                                 
        假设我的任务清单里有<total>个任务                                                  
        当我完成<finished>件任务之后                                                   
        那么我还剩下<left>件未完成的任务                                                   
                                                                              
        例子:                                                                   
                                                                              
      场景大纲: 完成任务       # todo.feature:12                                      
        假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)                         
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)                       
        那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int)                         
                                                                              
      场景大纲: 完成任务       # todo.feature:13                                      
        假设我的任务清单里有5个任务 # TodoStep.iHaveSomeTasks(int)                         
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)                       
        那么我还剩下4件未完成的任务 # TodoStep.iLeftSomeTasks(int)                         
                                                                              
    2 Scenarios (2 passed)                                                    
    6 Steps (6 passed)                                                        
    0m0.133s                                                                  
    
    

    总结

    从上面的例子里我们就可以看出自动化对生产力的提升帮助巨大。假设我们还需要测试100组数据,如果人肉手点的话,那么执行用例的人自然是痛不欲生,而且我们也没有办法完全保证数据输入的准确性,毕竟老虎也会有打盹的时候,何况是人。但是如果用自动化测试的话,增加数据无非就是在数据表中增加一些行,工作量不是特别大,而且可以比较容易的检查出数据是否准确。

    像这种以增加输入输出数据的方式增加用例的测试用例设计方法,我们可以称之为数据驱动。

    下一节我们将进一步的实现项目工程学上的自动化,我们将使用maven来搭建cucumber项目。

  • 相关阅读:
    如何将baidu地图中的baidu logo 去掉
    漂亮的圆角文本框 CSS3实现
    jQuery数字加减插件
    腾迅股票数据接口 http/javascript
    JS复制内容到剪贴板(兼容FF/Chrome/Safari所有浏览器)
    关于编写性能高效的javascript事件的技术
    想做web开发 就学JavaScript
    git的简单理解及基础操作命令
    《CSS权威指南》基础复习+查漏补缺
    TypeScript Writing .d.ts files(编写声明文件)
  • 原文地址:https://www.cnblogs.com/nbkhic/p/4886835.html
Copyright © 2011-2022 走看看