最近参加了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部分:
定义
执行
控制执行过程
项目结构如下,如上3部分对应的文件在截屏中有指示:

让我们来看看第一部分:定义
baidu.feture文件:
1Feature: Workshop baidu first case
2
3 @first
4 Scenario: Search cucumber
5 Given : I open a baidu homepage "http://www.baidu.com"
6 When : I input "cucumber" in searchbox
7 Then : I will see page title contains "cucumber"
在此处我们定义了一条用例,描述是说:当用户打开百度搜索页的时候,输入一个关键字"cucumber"并点按搜索,然后检查搜索结果页的标题是否包含了刚才输入的关键字。
然后这个定义是如何启动的呢?看第二部分:执行
build.gradle文件,重点看cucumberBaidu()方法:
1apply plugin: 'java'
2sourceCompatibility = 1.10
3version = '1.0'
4
5configurations {
6 cucumberRuntime {
7 extendsFrom testRuntime
8 }
9}
10
11repositories {
12 mavenCentral()
13}
14
15dependencies {
16 compile 'org.springframework:spring-context:4.2.1.RELEASE'
17 compile 'junit:junit:4.12'
18 compile 'org.seleniumhq.selenium:selenium-java:3.4.0'
19 compile 'info.cukes:cucumber-core:1.1.2'
20 compile 'info.cukes:cucumber-java:1.1.2'
21 compile 'info.cukes:cucumber-junit:1.1.2'
22
23 testCompile 'io.cucumber:cucumber-java:2.4.0'
24 testCompile 'io.cucumber:cucumber-junit:2.4.0'
25 testCompile 'junit:junit:4.12'
26}
27
28task cucumberBaidu() {
29 dependsOn assemble, compileTestJava
30 doLast {
31 javaexec {
32 main = "cucumber.api.cli.Main"
33 classpath = configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
34 args = ['-f', 'pretty', '--glue', 'baidu_step', 'src/test/resources/cucumber/baidu.feature']
35 }
36 }
37}
然后看第三部分,我们通过什么样的办法来自动化输入关键字并触发检索呢?
baidu_step.java文件,定义中引号的部分转换为java方法中的入参。如下:
1package baidu_step;
2
3import cucumber.api.PendingException;
4import cucumber.api.java.en.Given;
5import cucumber.api.java.en.Then;
6import cucumber.api.java.en.When;
7import org.openqa.selenium.By;
8import org.openqa.selenium.WebDriver;
9import org.openqa.selenium.WebElement;
10import org.openqa.selenium.chrome.ChromeDriver;
11
12/**
13 * Created by bianxh on 2018/3/31.
14 */
15public class baidu_step {
16
17 WebDriver driver = null;
18
19 public void initializeDriver()
20 {
21 driver = new ChromeDriver();
22 }
23
24 @Given("^: I open a baidu homepage \"([^\"]*)\"$")
25 public void _I_open_a_baidu_homepage(String open_url) throws Throwable {
26 initializeDriver();
27 driver.get(open_url);
28 }
29
30 @When("^: I input \"([^\"]*)\" in searchbox$")
31 public void _I_input_in_searchbox(String Keyword) throws Throwable {
32 WebElement element = driver.findElement(By.cssSelector("#kw"));
33 element.sendKeys(Keyword);
34 WebElement element1 = driver.findElement(By.cssSelector("input#su"));
35 // WebElement element1 = driver.findElement(By.cssSelector("input#su"));
36// WebElement element1 = driver.findElement(By.className("bg s_btn"));
37 element1.click();
38
39 }
40
41 @Then("^: I will see page title contains \"([^\"]*)\"$")
42 public void _I_will_see_page_title_contains(String Result) throws Throwable {
43// throw new PendingException();
44 Thread.sleep(2000);
45 System.out.println("title:" + driver.getTitle());
46 driver.getTitle().contains(Result);
47 driver.close();
48 }
49}
最后,我们在控制台执行:
1bianxh:group_five_art bianxh$ gradle cucumberBaidu
2> Task :cucumberBaidu
3WARNING: An illegal reflective access operation has occurred
4WARNING: 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
5WARNING: Please consider reporting this to the maintainers of cucumber.deps.com.thoughtworks.xstream.converters.collections.TreeMapConverter
6WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
7WARNING: All illegal access operations will be denied in a future release
8Starting ChromeDriver 2.36.540469 (1881fd7f8641508feb5166b7cae561d87723cfa8) on port 1468
9Only local connections are allowed.
104月 02, 2018 8:04:17 下午 org.openqa.selenium.remote.ProtocolHandshake createSession
11信息: Detected dialect: OSS
12title:cucumber_百度搜索
13Feature: Workshop baidu first case
14 @first
15 Scenario: Search cucumber # src/test/resources/cucumber/baidu.feature:4
16 Given : I open a baidu homepage "http://www.baidu.com" # baidu_step._I_open_a_baidu_homepage(String)
17 When : I input "cucumber" in searchbox # baidu_step._I_input_in_searchbox(String)
18 Then : I will see page title contains "cucumber" # baidu_step._I_will_see_page_title_contains(String)
19BUILD SUCCESSFUL in 7s
203 actionable tasks: 2 executed, 1 up-to-date
结果如下:

可以看到单个测试成功了。
注:通过drive.close();可以关闭chrome浏览器。
那如果我们要定义一组feature来测试呢?
定义如下:
1 @first_group
2 Scenario Outline: multi-data test
3 Given : I open a baidu homepage "<open_url>"
4 When : Input "<Keyword>"
5 Then : See "<Result>"
6 Examples:
7 |open_url | Keyword | Result |
8 |http://www.hao123.com | abc | abc |
9 |http://www.hao123.com | abc1 | abc1 |
注意参数用<>进行了定义,执行代码中对多出来的部分进行控制,新增的代码如下:
1// 2 feature
2 @When("^: Input \"([^\"]*)\"$")
3 public void _Input(String Keyword) throws Throwable {
4 WebElement element = driver.findElement(By.cssSelector("#search-input"));
5 element.sendKeys(Keyword);
6 WebElement element1 = driver.findElement(By.cssSelector("#search-form > div.button-wrapper.g-ib > input"));
7 element1.click();
8 }
9
10 @Then("^: See \"([^\"]*)\"$")
11 public void _See(String Result) throws Throwable {
12 Thread.sleep(2000);
13 System.out.println("title:" + driver.getTitle());
14 driver.getTitle().contains(Result);
15 driver.close();
16 }
注:关于截取网页元素的时候,可以通过chrome工具抓取selector来确保唯一性,这个一般可以应对后续的网页变化(网页更新时,测试用例代码改动较少)。

抓取后获取和使用办法如下:
1WebElement element1 = driver.findElement(By.cssSelector("input#su"));
2// 模拟点击事件
3element1.click();
最后,还是要感谢我们5组两位coach。谢谢!




