concordion学习笔记

concordion简要

  1. concordion的实现可以理解为活文档,自然语言,易读,包含很多examples。文档是html,所以构建出可以navigable structure的文档。 频繁的运行可以使文档和系统功能同步。如果系统行为发生了变化,相应的文档就会fail
  2. concordion文档不包含实现细节,把需求和实现分开,包含列子,实现方式可变,方便重构
  3. concordion是Junit的扩展
  4. 易学,基于specifications by example、
  5. 不需要特定的语言格式,使用自然语言,关注易读性
  6. 丰富的测试报告。 HTML格式的报告可以加入超链接和图片等多种
  7. 文档可以当成unit test来运行。具备和Junit测试一样的深度
  8. BA QA DEV可以一起合作写出文档
  9. 文档可以适用于不同级别 unit, component, subsystem, system
  10. 活文档可以用来定义story的验收条件,帮助PO来控制项目范围
  11. 提高测试覆盖率。不管实现如何,需求一定会被满足

concordion实现

一个concordion规格文档由两部分组成

  • 描述功能的html文件

  • 测试代码 (基于Junit)

两个文件应该在同一个文件夹下

为了两个文档的融合使用,html里面应该包含concordion的相关命令。这个命令以结点的属性形式出现


     
        

Hello World!

测试代码基于Junit,方法名需要和html中的名字一样,使用ConcordionRunner和Junit的RunWith来让concordion把相关的命令和类联系起来

package example;

import org.concordion.integration.junit4.ConcordionRunner;
import org.junit.runner.RunWith;

@RunWith(ConcordionRunner.class)
public class HelloWorldFixture {

     public String getGreeting() {
        return "Hello World!";
    }
}

测试报告输出默认目录是 system property: java.io.tmpdir.

concordion使用方法

concordion:assertEquals:验证执行方法的返回值和所期待的结果一致

concordion:set:定义一个变量

The greeting for user Bob will be: Hello Bob!

concordion:execute:可以执行一个测试代码里面的方法

一般来说,执行一个没有返回值的方法,要用set或是setUp开头。

If the time is 09:00AM then the greeting will say: Good Morning World!

利用#TEXT这个特殊变量可以简写上面的spec。可以不使用concordion:set。

在把#TEXT作为参数传给方法,后面接着#TEXT的值

        

If the time is 09:00AM then the greeting will say: Good Morning World!

执行一个有返回值的方法,可以使用方法接受返回值
        

The full name John Smith will be broken into first name John and last name Smith.

这里的#result是一个类,split()方法返回一个类对象. firstName and lastName 是类属性

处理不寻常的语句结构

当我们面对规格文档描述不利于concordion命令的实现的时候,我们可以把execute命令放到更上一层结点结构当中

The greeting "Hello Bob!" should be given to user Bob when he logs in.

execute命令的执行是有一定的顺序的
  1. 首先是处理所有child的set命令
  2. 然后是自己的命令
  3. 然后是自己child的execute命令
  4. 最后是所有child的assertEquals方法
concoirdion:execute在table里面的使用

concordion:execute on a

可以设置在table heading级别来执行table里面的每行数据

当我们需要表现同一个行为的不同列子的时候,需要重复同样的语句结构。这种情况下可以使用table来表达

            
Full Name First Name Last Name
John Smith John Smith
David Peterson David Peterson

但是这样看起来也是比较重复的

所以concordion支持把concordion命令放在table heading级别,每一个td就会重复th的命令

下面的列子中,execute在table级别,set, assertEquals在table heading级别

            
Full Name First Name Last Name
John Smith John Smith
David Peterson David Peterson
concordion:execute在list上的使用

concordion:execute on a 可以给方法传递有层级的测试数据

当我们使用concordion在列表上的时候,比如ol 和 ul 元素, 该execute命令会执行在每一个列表元素上。
这个特性能帮我们设置有层级结构的测试数据

  1. Europe
    • Austria
      1. Vienna
    • UK
      • England
      • Scotland
    • France
  2. Australia

当执行到

    Username
    john.lennon
    ringo.starr
    george.harrison
    paul.mccartney

    Searching for "arr" will return:

    Matching Usernames
    george.harrison
    ringo.starr

    verifyRows的语法是: var : iteration_object #loopVar : expression

    expression是一个可迭代对象并且对象里面的元素对象顺序是已知的

    loopVar能够访问返回迭代对象的每一个元素,并且支持使用assertEquals去进行验证

    从expression返回的对象元素的顺序必须与行里设置的测试数据是一致的

    注解

    当我们把未完成的测试代码加入到我们的build当中,为了避免使build失效,我们可以加注解。

    • @ExpectedToPass 期待pass
    • @ExpectedToFail 期待fail
    • @Unimplemented 未完成
    For example:
    
    import org.concordion.api.ExpectedToFail;
    import org.concordion.integration.junit4.ConcordionRunner;
    import org.junit.runner.RunWith;
    
    @ExpectedToFail
    @RunWith(ConcordionRunner.class)
    public class GreetingTest {
    
      public String greetingFor(String firstName) {
            return "TODO";
      }
    }
    
    Fail-Fast

    当出现异常的时候,concordion会继续执行剩下的测试来展示所有的问题。使用Fail-Fast可以使concordion遇到第一个异常时就停止继续执行,跳出测试。

    方法是使用注解

    Fail-Fast有一个参数onExceptionType,这个参数是一个列表,列表里面的内容是各种异常类型,只有在执行当中遇到列表里面的异常类型,Fail-Fast才会生效。

    import org.concordion.api.FailFast;
    import org.concordion.integration.junit4.ConcordionRunner;
    import org.junit.runner.RunWith;
    
    @FailFast(onExceptionType={DatabaseUnavailableException.class, IOException.class})
    public class MyDataTest {
    
       public void connectToDatabase() {
            ....
       }
    }
    
    concordion:run 可以使该文档link到其他文档,并且run
    concordion:assertTrue 验证执行方法返回值是不是True
    concordion:assertFalse 验证执行方法返回值是不是False

    When user Bob logs in, the greeting will be: Hello Bob!

    The first name starts with B.

    The first name starts with C.

    支持Junit4.5以上, 测试代码使用ConcordionRunner
    package example;
    
    import org.concordion.integration.junit4.ConcordionRunner;
    import org.junit.runner.RunWith;
    
    @RunWith(ConcordionRunner.class)
    public class HelloWorldFixture {
    
        public String getGreeting() {
            return "Hello World!";
        }
    }
    

    返回值的类型 也可以是map

        public Map split(String fullName) {
            String[] words = fullName.split(" ");
            Map results = new HashMap();
            results.put("firstName", words[0]);
            results.put("lastName", words[1]);
            return results;
        }
    

    可以使用MultiValueResult来返回多个结果

    MultiValueResult 是concordion api的一个类

    package example;
    
    import org.concordion.api.MultiValueResult;
    import org.concordion.integration.junit4.ConcordionRunner;
    import org.junit.runner.RunWith;
    
    @RunWith(ConcordionRunner.class)
    public class SplittingNamesTest {
    
        public MultiValueResult split(String fullName) {
            String[] words = fullName.split(" ");
            return multiValueResult()
                    .with("firstName", words[0])
                    .with("lastName", words[1]);
        }
    }
    

    使用这个方法,我们可以在测试代码里面设置比如属性firstName的值,然后在specification里面直接调用

    John

    特殊变量 #HREF

    当我们想使用外部文件作为测试数据时,可以使用#HREF指向一个link,这个link就是数据源文件。

    比如我们把测试数据放在一个csv文件里面,我们就可以在html中写一个link,然后在这个中使用concordion命令和#HREF

    test file
    
    blah.csv
    
    Write specifications, not scripts

    只在Specification中描述feature和功能,不要有太详细的步骤,具体的实现细节都应该放在测试代码里面。

    Specification可以说是high level的测试脚本

    concordion:echo is able to print the variable to test report
      

    Example

    When user World logs in, the greeting will be: Hello, World!

    Username: # this is the variable created in specification file

    Example

    When user World logs in, the greeting will be: Hello, World!

    Username: # this is the class variable from test fixture code

    @RunWith(ConcordionRunner.class)
    public class HelloWorldFixture {
    
        public String username = "username";
    
        public String greetingFor(String firstName) {
            return new Greeter().greetingFor(firstName);
        }
    }
    

你可能感兴趣的:(concordion学习笔记)