行为驱动开发入门 - yiyixiaozhi/readingNotes GitHub Wiki

Behavior Driven Development 行为驱动开发入门

最近参加了Toughtworks的一次线下Workshop,有幸头一次体验并实战了BDD。感觉非常不错。

主题是:从0到1搭建自动化测试框架。围绕BDD进行,就如题目所说,翻译过来就是行为驱动开发,这是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。BDD最初是由Dan North在2003年命名,它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。在过去数年里,它得到了很大的发展。

在我的理解中,这个定义过程,弱化了当前需求职能在对接业务和研发过程中的翻译属性,而强化了解释属性。解释源头可以交由客户来定义。

前期基础环境准备如下:

Windows系统环境搭建请参考:https://note.youdao.com/share/id=1465ad112ca09d244710a68881d04ad2&type=note#/

Mac系统环境搭建请参考: http://note.youdao.com/noteshare?id=1fd3e24d52158463b05d705403a9c154

为了方便gradle项目的构建,此处使用的IDE是IntellJ IDEA ,我的版本是15。

拿一个最小的测试用例来讲,可以将期解剖为3部分:

  1. 定义

  2. 执行

  3. 控制执行过程

项目结构如下,如上3部分对应的文件在截屏中有指示:

img

让我们来看看第一部分:定义

baidu.feture文件:

Feature: Workshop baidu first case

  @first
  Scenario: Search cucumber
    Given : I open a baidu homepage "http://www.baidu.com"
    When : I input "cucumber" in searchbox
    Then : I will see page title contains "cucumber"

在此处我们定义了一条用例,描述是说:当用户打开百度搜索页的时候,输入一个关键字"cucumber"并点按搜索,然后检查搜索结果页的标题是否包含了刚才输入的关键字。

然后这个定义是如何启动的呢?看第二部分:执行

build.gradle文件,重点看cucumberBaidu()方法:

apply plugin: 'java'
sourceCompatibility = 1.10
version = '1.0'

configurations {
    cucumberRuntime {
        extendsFrom testRuntime
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework:spring-context:4.2.1.RELEASE'
    compile 'junit:junit:4.12'
    compile 'org.seleniumhq.selenium:selenium-java:3.4.0'
    compile 'info.cukes:cucumber-core:1.1.2'
    compile 'info.cukes:cucumber-java:1.1.2'
    compile 'info.cukes:cucumber-junit:1.1.2'

    testCompile 'io.cucumber:cucumber-java:2.4.0'
    testCompile 'io.cucumber:cucumber-junit:2.4.0'
    testCompile 'junit:junit:4.12'
}

task cucumberBaidu() {
    dependsOn assemble, compileTestJava
    doLast {
        javaexec {
            main = "cucumber.api.cli.Main"
            classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
            args = ['-f', 'pretty', '--glue', 'baidu_step', 'src/test/resources/cucumber/baidu.feature']
        }
    }
}

然后看第三部分,我们通过什么样的办法来自动化输入关键字并触发检索呢?

baidu_step.java文件,定义中引号的部分转换为java方法中的入参。如下:

package baidu_step;

import cucumber.api.PendingException;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

/**
 * Created by bianxh on 2018/3/31.
 */
public class baidu_step {

    WebDriver driver = null;

    public void initializeDriver()
    {
        driver = new ChromeDriver();
    }

    @Given("^: I open a baidu homepage \"([^\"]*)\"$")
    public void _I_open_a_baidu_homepage(String open_url) throws Throwable {
        initializeDriver();
        driver.get(open_url);
    }

    @When("^: I input \"([^\"]*)\" in searchbox$")
    public void _I_input_in_searchbox(String Keyword) throws Throwable {
        WebElement element = driver.findElement(By.cssSelector("#kw"));
        element.sendKeys(Keyword);
        WebElement element1 = driver.findElement(By.cssSelector("input#su"));
        //        WebElement element1 = driver.findElement(By.cssSelector("input#su"));
//        WebElement element1 = driver.findElement(By.className("bg s_btn"));
        element1.click();

    }

    @Then("^: I will see page title contains \"([^\"]*)\"$")
    public void _I_will_see_page_title_contains(String Result) throws Throwable {
//        throw new PendingException();
        Thread.sleep(2000);
        System.out.println("title:" + driver.getTitle());
        driver.getTitle().contains(Result);
        driver.close();
    }
}

最后,我们在控制台执行:

bianxh:group_five_art bianxh$ gradle cucumberBaidu

> Task :cucumberBaidu 
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by cucumber.deps.com.thoughtworks.xstream.converters.collections.TreeMapConverter (file:/Users/bianxh/.gradle/caches/modules-2/files-2.1/info.cukes/cucumber-core/1.1.2/423d83b02d6a2409c21264023ad7b87d625be59e/cucumber-core-1.1.2.jar) to field java.util.TreeMap.comparator
WARNING: Please consider reporting this to the maintainers of cucumber.deps.com.thoughtworks.xstream.converters.collections.TreeMapConverter
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Starting ChromeDriver 2.36.540469 (1881fd7f8641508feb5166b7cae561d87723cfa8) on port 1468
Only local connections are allowed.
4月 02, 2018 8:04:17 下午 org.openqa.selenium.remote.ProtocolHandshake createSession
信息: Detected dialect: OSS
title:cucumber_百度搜索

Feature: Workshop baidu first case

  @first
  Scenario: Search cucumber                                # src/test/resources/cucumber/baidu.feature:4
    Given : I open a baidu homepage "http://www.baidu.com" # baidu_step._I_open_a_baidu_homepage(String)
    When : I input "cucumber" in searchbox                 # baidu_step._I_input_in_searchbox(String)
    Then : I will see page title contains "cucumber"       # baidu_step._I_will_see_page_title_contains(String)


BUILD SUCCESSFUL in 7s
3 actionable tasks: 2 executed, 1 up-to-date

结果如下:

img

可以看到单个测试成功了。

注:通过drive.close();可以关闭chrome浏览器。

那如果我们要定义一组feature来测试呢?

定义如下:

  @first_group
  Scenario Outline: multi-data test
    Given : I open a baidu homepage "<open_url>"
    When : Input "<Keyword>"
    Then : See "<Result>"

  Examples:
    |open_url | Keyword | Result |
    |http://www.hao123.com | abc | abc |
    |http://www.hao123.com | abc1 | abc1 |

注意参数用<>进行了定义,执行代码中对多出来的部分进行控制,新增的代码如下:

// 2 feature
    @When("^: Input \"([^\"]*)\"$")
    public void _Input(String Keyword) throws Throwable {
        WebElement element = driver.findElement(By.cssSelector("#search-input"));
        element.sendKeys(Keyword);
        WebElement element1 = driver.findElement(By.cssSelector("#search-form > div.button-wrapper.g-ib > input"));
        element1.click();
    }

    @Then("^: See \"([^\"]*)\"$")
    public void _See(String Result) throws Throwable {
        Thread.sleep(2000);
        System.out.println("title:" + driver.getTitle());
        driver.getTitle().contains(Result);
        driver.close();
    }

注:关于截取网页元素的时候,可以通过chrome工具抓取selector来确保唯一性,这个一般可以应对后续的网页变化(网页更新时,测试用例代码改动较少)。

img

抓取后获取和使用办法如下:

WebElement element1 = driver.findElement(By.cssSelector("input#su")); // 模拟点击事件 element1.click();

⚠️ **GitHub.com Fallback** ⚠️