目录

回顾

在上一节中,我大致的介绍了一下cucumber的特点,以及基于ruby和JavaScript下关于cucumber环境的配置,如果你还没有进行相关的了解或者环境的配置,你可以点击这里来进行了解一下

在本节中,我将借用一个场景来对cucumber的一些特点来进行描述

HelloWorld

1.cucumber的目录结构

cucumber的执行需要特定的目录结构的。我们首先需要先创建这样的一个目录结构:

mkdir HelloWorld
cd HelloWorld
mkdir features
cd features
touch hello_cucumber.feature
mkdir step_definitions
cd step_definitions
touch hello_cucumber.rb

通过上面的命令创建好如下图的目录层级结构:

在图中,HelloWorld这个目录下面包含了一个名为features目录,在features目录下面有一个名为hello_cucumber.feature的文件以及一个名为step_definitions的目录,在step_definitions这个目录下面有一个名为hello_cucumber.rb的文件。下面解释下各个目录以及文件的作用:

HelloWorld

这个根目录可以理解为工程所在的目录,存放了所有需要用到的文件和资源

features

这个目录下面包含了所有的场景文件和实现场景文件中描述的步骤文件

hello_cucumber.feature

这个文件包含了场景的描述,也就是对测试用例的描述,里面一般交代了测试的前提和测试需要的条件并且当发生某一事件的时候能够得到一个什么样的结果,这些描述一般使用很易懂的自然语言来进行描述它,所以技术或者非技术人员都能看懂

在features目录下可以包含一个或者多个feature文件,一般情况下,对于具有相同性质的测试场景会放到同一个feature文件中,以便于维护

step_definitions

这个目录包含了实现测试场景描述的步骤文件

hello_cucumber.rb

这个文件包含对应的测试场景的实现代码,这个步骤文件取决于你使用的什么语言,我这里是使用的ruby

2.代码

代码主要是包含在hello_cucumber.feature和hello_cucumber.rb文件中

hello_cucumber.feature

这里我们主要描述这样一个场景,以平常一个登陆系统来做为例子吧。在测试中的话,首先会有对这个登陆系统的描述和一些基本的需求。主要是作为一个登陆系统的使用者,希望登陆系统的运作正常。正常的使用中,一个登陆场景如下:

  前置条件:进入到登陆页面

  事件: 在用户名输入框输入正确的用户名,在密码框输入正确的密码,点击确定

  预期结果:成功登陆

这是一个比较简单的测试用例,看看在cucumber中是如何实现它的。

首先我们先要将这个用例转换成cucumber所知道的描述语言,这部分描述是在feature文件中来进行实现的。代码如下:

Feature: Login
    Login system should work fine

    Scenario: Login with right username and right password
        Given I open the login page
        When I set username with a right username
        And I set password with a right password
        And I click the login button
        Then I see login success

代码中的Feature,Scenario,Given,And,When,Then都是cucumber中的关键字,Feature说明了这个测试场景的大体情况和一些基本的描述,一般由需求人员和测试人员去完成,Scenario指明了具体场景的标题,以及这个场景是用来干什么的。Given给出了具体的测试场景的条件,When给出了事件,And表示一个连接,Then是预期的结果。在一个feature文件中可以包含多个Scenario。

hello_cucumber.rb

这个文件主要包含了feature文件中描述的场景的实现,代码如下

Given /^I open the login page$/ do
    puts "open login page"
end

When /^I set username with a right username$/ do
    puts "set a right username"
end

When /^I set password with a right password$/ do
    puts "set a right password"
end

When /^I click the login button$/ do
    puts "click login button"
end

Then /^I see login success$/ do
    puts "login success"
end

在feature文件中的每一个步骤都需要一个相应的ruby代码来与之对应,如果没有对应的实现步骤,则在执行用例的时候会出现找不到步骤的异常。在feature寻找步骤的时候,是通过正则表达式来进行匹配的,step文件中的Given /^...$/会匹配feature文件中符合这个正则的步骤。这个匹配是会忽略Given  When之类的关键字的。如果在匹配的时候feature中的步骤没有找到step中对应的实现,则才执行的时候会报出忽略这一个步骤的异常而导致用户不会通过的。

3.运行用例

在命令行下进入到HelloWorld这个文件夹,然后输入命令 cucumber features/hello_cucumber.feature,结果如下:

从结果中看出,我们的用例全部的通过了。而且,我们在ruby文件(step文件)中的日志也都被打印出来了(天蓝色字)。

关于cucumber的命令,我会在后面仔细的来进行介绍的。 

扩展

通过上面的例子,我们已经可以使用cucumber的基本功能去实现一些用例了。当然前提还需要你去了解一些自己熟悉的语言的一些测试框架,不然单纯的cucumber并不能起到太大的作用。

现在我们来进行扩展一下上面的需求。依旧是登录的例子。测试用例如下:

  前置条件:进入到登陆页面

  事件: 在用户名输入框输入正确的用户名,在密码框输入错误的密码,点击确定

  预期结果:成功失败

根据刚刚所说的,写下的如下的feature:

Feature: Login
    Login system should work fine

    Scenario: Login with right username and error password
        Given I open the login page
        When I set username with a right username
        And I set password with a error password
        And I click the login button
        Then I see login fail

对应的step如下:

Given /^I open the login page$/ do
    puts "open login page"
end

When /^I set username with a right username$/ do
    puts "set a right username"
end

When /^I set password with a error password$/ do
    puts "set a error password"
end

When /^I click the login button$/ do
    puts "click login button"
end

Then /^I see login success$/ do
    puts "login fail"
end

代码写到这里就完成了,但是软件工程中一个重要的思想是复用,相同的东西是否能进行提取呢?在上面的2个例子中,在设置密码这个地方,步骤基本是相同的,只是输入的值不一样,我们是否可以将这2个步骤合成一个呢?答案是可以的。修改设置密码的step:

When /^I set password with a (.*) password$/ do |password|
    puts "set a #{password} password"
end

经过这样的修改后发现feature中密码中输入步骤也能匹配到step对应的实现,而且能将right或者是error提取成一个参数传入到实现中。

feature中的参数化使得我们很少做很多的重复工作。

聪明的你又想起了另外的一个需求,比如我们的登录的例子需要在chrome和firefox上面去执行。也许你会想,我们直接把上面的第一步(打开浏览器进入login页)修改成匹配chrome和firefox的通用步骤,然后其他的copy一遍就ok。这样做是ok的,然而cucumber提供了一种更为聪明的方法来进行这个操作。

feature如下:

Feature: Login
  Login system should work fine

  Scenario Outline: Login with right username and right password
    Given I use <browser> open the login page
    When I set username with a right username
    And I set password with a right password
    And I click the login button
    Then I see login success

    Examples:
      | browser |
      | chrome  |
      | firefox |

step如下:

Given /^I use (.*) open the login page$/ do |browser|
  puts "use #{browser} open login page"
end

When /^I set username with a right username$/ do
  puts "set a right username"
end

When /^I set password with a (.*) password$/ do |password|
  puts "set a #{password} password"
end

When /^I click the login button$/ do
  puts "click login button"
end

Then /^I see login success$/ do
  puts "login success"
end

在feature中,将前面Scenario改为了Scenario Outline,表示会有表格参数,参数使用<>来进行标注,在用例结束后Examples关键字中写入参数的具体值。这样后,在执行用例的时候,这个用例会被执行2次,每次执行会依次的采用Examples中的参数来进行执行,我们使用命令来执行看看结果:

从结果中可以看出我们执行了2个Scenario,打出的日志在Examples下面,第一个使用了chrome,第二个使用了firefox,和我们的预期是一样的。使用Examples能大幅的减轻我们的工作量。

在cucumber中还存在着另外一个table参数。我们看看他的使用场景。依旧是登录页,但是现在登录页和之前的不太一样,我们的系统是非常注重安全的,所以每一个用户登录的时候需要使用2个密码来进行登录,如果密码全部都正确才能登录成功。测试用例如下:

  前置条件:进入到登陆页面

  事件: 在用户名输入框输入正确的用户名,在第一个密码框输入正确的密码,在第二个密码框输入正确的密码,点击确定

  预期结果:成功登录

先看看feature:

Feature: Login
  Login system should work fine

  Scenario: Login with right username and right password
    Given I open the login page
    When I set username with a right username
    And I set password
      | id        | value  |
      | password1 | value1 |
      | password2 | value2 |
    And I click the login button
    Then I see login success

在看看step:

Given /^I open the login page$/ do
  puts "open login page"
end

When /^I set username with a right username$/ do
  puts "set a right username"
end

When /^I set password/ do |table|
  table.hashes.each do |item|
    puts "set #{item["id"]} to #{item["value"]}"
  end
end

When /^I click the login button$/ do
  puts "click login button"
end

Then /^I see login success$/ do
  puts "login success"
end

在feature中,我们也使用了一个表格的形式,在step中可以通过一个参数直接获取到这个表格中的所有的参数,并且将他们存入到了hash中,而我们可以对这个hash进行操作,然后获取我们想要的值。

执行用例后如下:

可以看到结果和我们想要的是一样的。

在cucumber中有时候还会使用到步骤的组合。依旧采用登录这个例子。他里面所有的feature和使用的第一个例子一样。我们仔细想想里面的步骤中的第一步(打开登录页面),它实际是由打开浏览器和跳转到登录页面这2个步骤实现的,现在我们不改变feature,看看step中如何把这一步分解成为2步:

Given /^I open the login page$/ do
  steps %{
    Given I launch the browser
    And I go to login page
  }
end

When /^I set username with a right username$/ do
  puts "set a right username"
end

When /^I set password with a right password/ do
  puts "set a right password"
end

When /^I click the login button$/ do
  puts "click login button"
end

Then /^I see login success$/ do
  puts "login success"
end

Given /^I launch the browser$/ do
  puts "launch browser"
end

Given /^I go to login page$/ do
  puts "go to login page"
end

在上面的step中,我们将之前的一步分解成了2步,这2步又被另外的一个step所调用。你也可以用下面的方式来实现它:

Given /^I open the login page$/ do
    step "I launch the browser"
    step "I go to login page"
end

看看执行的结果:

结果如我们所预期的一样。

 

关于cucumber的基本语法就讲到这里了,如果感觉我有漏掉的希望大家可以提出。

posted on 2013-11-12 17:53  饭小  阅读(5551)  评论(2编辑  收藏  举报