zoukankan      html  css  js  c++  java
  • cucumber java从入门到精通(3)简单实现及断言

    cucumber java从入门到精通(3)简单实现及断言

    上一节里我们定义了step的java代码实现文件,step就是测试步骤及断言的集合,我们先定义出来,以后可以驱动开发以及在持续集成时重用。

    这一节我们将近距离细观一下所谓的step java实现。以下面的代码片段为例:

    public class TodoStep { //1                                                                             
        @假设("^我的任务清单里有(\d+)个任务$") //2                                                    
        public void iHaveSomeTasks(int totalTasks) throws Throwable { //3                        
            // Write code here that turns the phrase above into concrete actions     
            throw new PendingException();                                //4            
        }                                                                            
    }
    
    • //1 定义了public class,这没什么好说的;
    • //2 假设注解,这个注解表明下面的方法对应的也就是feature文件中我的任务清单里有xxxx个任务这个步骤;
    • //3 定义了具体实现feature文件步骤的方法,并从feature定义中取得传入参数,也就是xxxx个任务的具体值;
    • //4 抛出Pending异常,表明该步骤暂未实现,但来日方长,也许有天可以实现;

    cucmber执行顺序

    如果你对上面的代码尚有疑问,那么是时候看一下cucumber的执行顺序了,以下面代码片段为例:

    # feature
    假设 我的任务清单里有3个任务 // 1
    当 我完成1件任务之后 // 2
    那么 我还剩下2件未完成的任务 //3
    
    # step
    @假设("^我的任务清单里有(\d+)个任务$") //4                                                   
        public void iHaveSomeTasks(int totalTasks) throws Throwable { //5                       
            // Write code here that turns the phrase above into concrete actions     
            throw new PendingException();                                            
        }                                                                            
                                                                                     
    @当("^我完成(\d+)件任务之后$") //6                 
    public void iFinishSomeTasks(int finishedTasks) throws Throwable { //7                        
        // Write code here that turns the phrase above into concrete actions  
        throw new PendingException();                                            
    }                                                                            
                                                                                 
    @那么("^我还剩下(\d+)件未完成的任务$") //8                                                 
    public void iLeftSomeTasks(int leftTasks) throws Throwable { //9                        
        // Write code here that turns the phrase above into concrete actions        
        throw new PendingException();                                            
    }                                    
    
    • 当我们运行了run命令后(还记得上一节的run吗?其实就是执行了cucumber.api.cli.Main。),cucumber会去找feature文件,然后执行第1条feature语句,也就是//1
    • 执行//1的时候,cucumber会去找对应的step定义文件,寻寻觅觅的过程中,cucumber发现了对应的注解,也就是//4
    • //4告诉cucumber,下面紧接着定义的那个方法就是该feature对应的代码实现,于是cucumber再去执行//5
    • 在执行//5的时候,cucumber将3作为totalTasks这个参数传给//5
    • cucumber执行//5方法体中的内容并收到pending异常,于是该feature执行结束,转去执行下1条feature

    小练习:你能自己说明//2以及//3的执行顺序吗

    渐进重构之假装实现

    回忆一下我们的测试目标,我们要测试一个todo list,第1步也就是在测试背景或者叫做前置条件里,我们是这样描述的

    假设 我的任务清单里有3个任务 // 1
    

    这时候不妨假设我们有个TodoList类,该类有个getTotalTaskCount()方法返回任务清单中一共有多少条任务。基于这个想法,我们重构一下TodoStep.java文件

    
    @假设("^我的任务清单里有(\d+)个任务$")                                                   
        public void iHaveSomeTasks(int totalTasks) throws Throwable {                        
            // totalTasks == 3
            TodoList todo = new TodoList();
            assertEquals(todo.getTotalTaskCount(), totalTasks);
        }   
    

    在这里要说明的是注解中的(d)表示获取feature定义中的数字字符并把该数字(int)作为totalTasks参数传入iHaveSomeTasks方法。因此totalTaks应该等于3。另外assertEquals是jUnit的断言方法

    执行一下compile && run,得到下面的结果

    step_definitionsTodoStep.java:11: 错误: 找不到符号
                    TodoList todo = new TodoList();
                    ^
      符号:   类 TodoList
      位置: 类 TodoStep
    step_definitionsTodoStep.java:11: 错误: 找不到符号
                    TodoList todo = new TodoList();
                                        ^
      符号:   类 TodoList
      位置: 类 TodoStep
    2 个错误
    

    我们无法编译,因为TodoList这个类并没有定义。

    下面我们定义一下TodoList类。在implementation文件夹中新建1个名为TodoList.java的文件,该文件的实现是

    // TodoList.java
    package implementation;
    
    public class TodoList {
        public int getTotalTaskCount() {
            return 3;
        }
    }
    

    下面在TodoStep.java中导入TodoList类

    // TodoStep.java
    import implementation.TodoList;
    

    修改一下compile文件,因为这次我们需要TodoList.java文件,修改完成的版本应该是这样的

    # compile
    javac -cp "./jars/*;." step_definitionsTodoStep.java implementationTodoList.java
    # linux && unix
    javac -cp "./jars/*:." step_definitionsTodoStep.java implementationTodoList.java
    

    然后运行compile && run,得到下面的结果

    #language: zh-CN
    功能: 任务管理
    
      场景: 完成1件任务       # todo.feature:5
        假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)
          cucumber.api.PendingException: TODO: implement me
            at step_definitions.TodoStep.iFinishSomeTasks(TodoStep.java:19)
            at ?.当我完成1件任务之后(todo.feature:7)
    
        那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int)
    
    1 Scenarios (1 pending)
    3 Steps (1 skipped, 1 pending, 1 passed)
    0m0.128s
    
    cucumber.api.PendingException: TODO: implement me
            at step_definitions.TodoStep.iFinishSomeTasks(TodoStep.java:19)
            at ?.当我完成1件任务之后(todo.feature:7)
    

    我们看到有1个step pass了,那就是我们刚定义前置条件step,所以到现在为止我们干的不错!我们有了feature文件,该文件描述了需求和测试用例,我们完成了测试step中的前置条件,并且我们实现了1个真正的TodoList类,尽管这个类目前还是嗷嗷待哺,不过当我们实现更多的step之后,TodoList类的功能会进一步完善,直到满足用户需求。实际上我们现在做的就是BDD,用测试去驱动开发。

    可能有的同学会对此不屑一顾,TodoList类到目前为止只是自欺欺人的实现了1个返回int型数字3的方法,离我们所要的任务清单还相差十万八千里。其实不用担心这个,我们的TodoList类没有实现具体的功能是因为我们实现的测试步骤还不够多,我们接收到的需求还只是那么一点点,当我们实现更多步骤之后,TodoList自然会羽翼丰满。这就是所谓的最小实现原则。

    渐进重构,重构剩下的2个步骤

    下面我们重构剩下的2个步骤。

    我们先假设TodoList有1个finishTask方法,每次调用这个方法就会完成n个任务,n从参数传入。于是剩余的任务就会减去n。
    我们再假设TodoList有1个getRestTasksCount方法,调用这个方法可以获取剩下的task的数量。
    重构完成以后,我们的代码如下所示

    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();
            assertEquals(todo.getTotalTaskCount(), totalTasks);
        }                                                                            
                                                                                     
        @当("^我完成(\d+)件任务之后$")                                                       
        public void iFinishSomeTasks(int finishedTasks) throws Throwable {                           
            // Write code here that turns the phrase above into concrete actions     
            todo.finisheTask(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.java文件。

    我们先实现finishedTask方法,这个方法的方法体是空,表示什么都不做;
    然后实现getRestTasksCount方法,这个方法简单的返回2就好;

    #TodoList.java
    package implementation;
    
    public class TodoList {
        public int getTotalTaskCount() {
            return 3;
        }
        
        public void finishTask(int count) {
            
        }
        
        public int getRestTasksCount() {
            return 2;
        }
    }
    

    再次运行compile && run

    可以看到,我们的step全部运行通过了

    #language: zh-CN                                                      
    功能: 任务管理                                                              
                                                                          
      场景: 完成1件任务       # todo.feature:5                                   
        假设我的任务清单里有3个任务 # TodoStep.iHaveSomeTasks(int)                     
        当我完成1件任务之后     # TodoStep.iFinishSomeTasks(int)                   
        那么我还剩下2件未完成的任务 # TodoStep.iLeftSomeTasks(int)                     
                                                                          
    1 Scenarios (1 passed)                                                
    3 Steps (3 passed)                                                    
    0m0.132s                                                              
    

    总结

    从这一节可以看到,我们的cucumber BDD行为驱动开发的流程是这样的:

    1. Describe behaviour in plain text
    2. Write a step definition in Java
    3. Run and watch it fail
    4. Write code to make the step pass
    5. Run again and see the step pass
    6. Repeat 2-5 until green like a cuke

    小练习:翻译上面的6句话,弄清楚具体的意思。对照教程,请指出每一步与教程中所对应的代码

    我们简单实现了TodoList类,目前的TodoList类是满足我们的feature的需求的。下一节我们将增加更多的测试数据,以便完善TodoList类。

  • 相关阅读:
    仿QQ消息弹出框
    WPF,Silverlight与XAML读书笔记第十六 资源之二进制资源
    WPF,Silverlight与XAML读书笔记第十七 资源之逻辑资源
    WPF,Silverlight与XAML读书笔记第十 WPF框架与工作方式介绍
    WPF,Silverlight与XAML读书笔记第五 XAML与传统过程式代码的关系
    再谈ASP.NET第七 跨应用、跨服务器的表单验证
    WPF,Silverlight与XAML读书笔记第六 WPF新概念之一逻辑树与可视树
    WPF,Silverlight与XAML读书笔记第八 WPF新概念之三路由事件
    WPF,Silverlight与XAML读书笔记第十四 独立存储&部署
    WPF,Silverlight与XAML读书笔记第七 WPF新概念之二依赖属性
  • 原文地址:https://www.cnblogs.com/nbkhic/p/4884171.html
Copyright © 2011-2022 走看看