capybara5--实现你第一个自动化场景

实现你第一个自动化场景

万事俱备,只欠东风,让我们来实现我们一个自动化测试场景,百度上搜索capybara。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe "i want search capybara", :type => :feature do
context 'i need to open browser' do
it 'should be has an browser'
end
context 'when i input baidu url into the browser textarea' do
it 'should be redirect to the baidu homepage'
end
context 'when i input the capybara into search text ' do
it 'responds with 200 and show the result'
end
end

rspec 与 cucumber完整的使用指南并不包含在本书的范畴,但我会给大家讲解基本的项目架构与语法,帮助大家更好的理解capybara和自动化测试的规范。

rspec方式

对于基于rspec框架的项目,我们首先需要创建spec目录,来将我们的测试文件放置当中,这样做使我们项目代码结构更加清晰。
此外我们还需要创建spec/rspec_help.rb文件帮助我们设置测试的配置信息。

如果你的项目是基于rails的,那么就更简单了,你只需要执行以下命令,系统就帮助我们完成这些基础配置和文件的生成

1
rails generate rspec:install
1
2
3
.rspec
spec/spec_helper.rb
spec/rails_helper.rb

让我正式开始
首先我们需要配置spec_helper.rb文件,
将以下内容复制到spec_helper.rb中,稍后章节中我将具体介绍配置文件中每条代码实际的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
require "capybara/rspec"
require "capybara"
require "rspec"
require 'pry'
Capybara.default_driver = :selenium
Capybara.app_host = 'http://www.baidu.com'
Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(
app, :browser => :chrome )
end
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# The generated `.rspec` file contains `--require spec_helper` which will cause
# this file to always be loaded, without a need to explicitly require it in any
# files.
#
# Given that it is always loaded, you are encouraged to keep this file as
# light-weight as possible. Requiring heavyweight dependencies from this file
# will add to the boot time of your test suite on EVERY test run, even for an
# individual file that may not need all of that loaded. Instead, consider making
# a separate helper file that requires the additional dependencies and performs
# the additional setup, and require it from the spec files that actually need
# it.
#
# The `.rspec` file also contains a few flags that are not defaults but that
# users commonly want.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
# rspec-expectations config goes here. You can use an alternate
# assertion/expectation library such as wrong or the stdlib/minitest
# assertions if you prefer.
config.expect_with :rspec do |expectations|
# This option will default to `true` in RSpec 4. It makes the `description`
# and `failure_message` of custom matchers include text for helper methods
# defined using `chain`, e.g.:
# be_bigger_than(2).and_smaller_than(4).description
# # => "be bigger than 2 and smaller than 4"
# ...rather than:
# # => "be bigger than 2"
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
# rspec-mocks config goes here. You can use an alternate test double
# library (such as bogus or mocha) by changing the `mock_with` option here.
config.mock_with :rspec do |mocks|
# Prevents you from mocking or stubbing a method that does not exist on
# a real object. This is generally recommended, and will default to
# `true` in RSpec 4.
mocks.verify_partial_doubles = true
end
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
# have no way to turn it off -- the option exists only for backwards
# compatibility in RSpec 3). It causes shared context metadata to be
# inherited by the metadata hash of host groups and examples, rather than
# triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content.
=begin
# This allows you to limit a spec run to individual examples or groups
# you care about by tagging them with `:focus` metadata. When nothing
# is tagged with `:focus`, all examples get run. RSpec also provides
# aliases for `it`, `describe`, and `context` that include `:focus`
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
config.filter_run_when_matching :focus
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options. We recommend
# you configure your source control system to ignore this file.
config.example_status_persistence_file_path = "spec/examples.txt"
# Limits the available syntax to the non-monkey patched syntax that is
# recommended. For more details, see:
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
config.disable_monkey_patching!
# This setting enables warnings. It's recommended, but in some cases may
# be too noisy due to issues in dependencies.
config.warnings = true
# Many RSpec users commonly either run the entire suite or an individual
# file, and it's useful to allow more verbose output when running an
# individual spec file.
if config.files_to_run.one?
# Use the documentation formatter for detailed output,
# unless a formatter has already been configured
# (e.g. via a command-line flag).
config.default_formatter = 'doc'
end
# Print the 10 slowest examples and example groups at the
# end of the spec run, to help surface which specs are running
# particularly slow.
config.profile_examples = 10
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = :random
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
# test failures related to randomization by passing the same `--seed` value
# as the one that triggered the failure.
Kernel.srand config.seed
=end
end

我们执行以下命令来运行我们的测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [22:00:31]
$ rspec ./spec/features/baidusearch_spec.rb
***
Pending: (Failures listed here are expected and do not affect your suite's status)
1) i want search capybara i need to open browser should be has an browser
# Not yet implemented
# ./spec/features/bb_spec.rb:9
2) i want search capybara when i input baidu url into the browser textarea should be redirect to the baidu homepage
# Not yet implemented
# ./spec/features/bb_spec.rb:16
3) i want search capybara when i input the capybara into search text responds with 200 and show the result
# Not yet implemented
# ./spec/features/bb_spec.rb:20
Finished in 0.00059 seconds (files took 1.37 seconds to load)
3 examples, 0 failures, 3 pending

我们从测试结果能看出,我们并没有实现任何具体操作步骤,接下就让我们完成这些工作。
将我们测试文件baidusearch_spec.rb修改为以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
describe "i want search capybara", :type => :feature do
context "the page" do
before do
page.driver.browser.manage.window.resize_to(1440,900) #Mention it here
visit "/"
end
context 'i need to open browser' do
it 'should be has an browser' do
expect(page.driver.browser.browser).to eq :chrome
end
end
context 'when i input baidu url into the browser textarea' do
it 'should be redirect to the baidu homepage' do
expect(page.driver.current_url).to eq('https://www.baidu.com/')
end
end
context 'when i input the capybara into search text ' do
it ' show search the result' do
fill_in 'kw', :with => 'capybara'
click_button '百度一下'
expect(page).to have_content 'Capybara - Wikipedia, the free encyclopedia'
end
end
end
end

再次运行测试
这时浏览器打开并且按照我们测试文件中具体步骤一步一步进行操作和验证,全部通过

1
2
3
4
5
6
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [22:26:15] C:1
$ rspec ./spec/features/baidusearch_spec.rb
...
Finished in 5.46 seconds (files took 3.81 seconds to load)
3 examples, 0 failures

cucumber方式

首先我们运行cucumber查看cucumber是否安装正确

1
2
3
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [13:42:41]
$ cucumber
No such file or directory - features. You can use `cucumber --init` to get started.

当我们看到以上提示没有文件或者目录时,我们就可以确认cucumber安装成功
接下来让我们初始化cucumber测试目录结构

1
2
3
4
5
6
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [11:24:56] C:2
$ cucumber --init
create features
create features/step_definitions
create features/support
create features/support/env.rb

这样就完成目录结构的初始化,我们要将所有feature文件都放到features目录下
support目录下,使我们需要的一些配置文件.

stepdefinitions目录下,是对feature文件中的具体操作实现。因为我们都知道feature文件中只是做了一些描述,具体的执行是放到step definitions下面的文件里面的。
接下来,我们用cucumber完成和上面spec一样的场景。

1
2
3
4
5
Feature: Search for capybara on baidu
Scenario: Search for website of baidu
Given I am on the baidu home page
When I search for "capybara"
Then website of search result are returned

在命令行中运行cucumber命令,
运行上述测试用例,便可以看到下列输出,你可以使用下面的代码段实现步骤定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [14:25:16] C:1
$ cucumber
Feature: Search for capybara on baidu
Scenario: Search for website of baidu # features/baidu_search_capybara.feature:2
Given I am on the baidu home page # features/baidu_search_capybara.feature:3
When I search for "capybara" # features/baidu_search_capybara.feature:4
Then website of search result are returned # features/baidu_search_capybara.feature:5
1 scenario (1 undefined)
3 steps (3 undefined)
0m0.395s
You can implement step definitions for undefined steps with these snippets:
Given(/^I am on the baidu home page$/) do
pending # Write code here that turns the phrase above into concrete actions
end
When(/^I search for "([^"]*)"$/) do |arg1|
pending # Write code here that turns the phrase above into concrete actions
end
Then(/^website of search result are returned$/) do
pending # Write code here that turns the phrase above into concrete actions
end

复制以上代码到具体step.rb文件中,我们新建文件capybara_search_step.rb。
再次运行cucumber命令
现在,您将看到命令行报告,这些步骤已经存在但没有实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [14:25:22]
$ cucumber
Feature: Search for capybara on baidu
Scenario: Search for website of baidu # features/baidu_search_capybara.feature:2
Given I am on the baidu home page # features/step_definitions/capybara_search_step.rb:1
TODO (Cucumber::Pending)
./features/step_definitions/capybara_search_step.rb:2:in `/^I am on the baidu home page$/'
features/baidu_search_capybara.feature:3:in `Given I am on the baidu home page'
When I search for "capybara" # features/step_definitions/capybara_search_step.rb:5
Then website of search result are returned # features/step_definitions/capybara_search_step.rb:9
1 scenario (1 pending)
3 steps (2 skipped, 1 pending)
0m0.152s

接下来,我们通过增加少量的代码来使我们自动化运行起来。
首先确保你的env.rb文件配置如下:

1
2
require 'capybara/cucumber'
Capybara.default_driver = :selenium

首先,我们在文件中添加require ‘capybara/cucumber’;
这是我们必须需要加载文件。
之后我们需要告诉capybara使用selenium驱动:

1
Capybara.default_driver = :selenium

再次重申,capybara只是充当一个中间件,适配不同的dirver,使我们能够兼容任何的驱动程序,例如(Selecting the Driver,
RackTest,
Selenium,
Capybara-webkit,
Poltergeist)这里我们选择selemium webdriver 是因为它是目前最流行的开源浏览器自动化工具,很多公司的自动化框架都是基于它之上的二次开发而成,例如appium,淘宝的Automan X,网易Dagger等。

如果我们不设置驱动,将会得到以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# lqi @ CNlqi in ~/work/test/capybara_demo on git:master x [17:13:47] C:2
$ cucumber
rack-test requires a rack application, but none was given (ArgumentError)
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/capybara-2.8.0/lib/capybara/rack_test/driver.rb:17:in `initialize'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/capybara-2.8.0/lib/capybara.rb:509:in `new'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/capybara-2.8.0/lib/capybara.rb:509:in `block in <top (required)>'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/capybara-2.8.0/lib/capybara/session.rb:85:in `call'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/capybara-2.8.0/lib/capybara/session.rb:85:in `driver'
/Users/lqi/work/test/capybara_demo/features/support/env.rb:12:in `<top (required)>'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/rb_support/rb_language.rb:96:in `load'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/rb_support/rb_language.rb:96:in `load_code_file'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime/support_code.rb:142:in `load_file'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime/support_code.rb:84:in `block in load_files!'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime/support_code.rb:83:in `each'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime/support_code.rb:83:in `load_files!'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime.rb:253:in `load_step_definitions'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/runtime.rb:61:in `run!'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/lib/cucumber/cli/main.rb:32:in `execute!'
/Users/lqi/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/cucumber-2.4.0/bin/cucumber:8:in `<top (required)>'
/Users/lqi/.rbenv/versions/2.2.2/bin/cucumber:23:in `load'
/Users/lqi/.rbenv/versions/2.2.2/bin/cucumber:23:in `<main>'

默认情况下,capybara被用来测试rack 应用。rack 是一个设计非常巧妙的中间件,被用作在rails与sinatra框架做全栈的Client/Server集成测试,并且没有什么HTTP的开销,从而是测试能够快速运行。稍后,我们将详细介绍capybara如何测试在rails与sinatra的应用测试。
接下来我们的任务就是你在步骤定义用Ruby代码调用capybaraAPI驱动测试。
将以下代码复制到你的capybara_search_step.rb中

1
2
3
4
5
6
7
8
9
10
11
12
13
Given(/^I am on the baidu home page$/) do
visit 'https://www.baidu.com/'
end
When(/^I search for "([^"]*)"$/) do |search_term|
fill_in 'kw', :with => search_term
click_on 'su'
end
Then(/^website of search result are returned$/) do
page.should have_content 'capybara'
end

在我们分析代码之前,让我们先运行测试,看看会发生什么。像往常一样,在命令行中输入cucumber。
希望你能看到chrome打开,并且跳转到baidu首页,之后搜索到capybara结果并返回。
恭喜你,你已经利用capybara完成了第一个全栈的集成测试。

在我们深入了解capybara丰富的API之前,让我们简要的回顾一下每个步骤的内容,这里你可以看到capybra如何优雅而简洁使用api,代码字面含义已经向我们展示它在干什么,不需要多解释。cucumber和capybra配合起来使用,具有良好的可读性,项目中的任何一个角色都能够当前场景做了哪些事情,非常适合应用于自动化和维护工作。
第一行代码告诉capybara通知driver(selenium webdriver)
打开一个浏览器并跳转到我们提供的链接地址:

1
visit 'https://www.baidu.com/'

接下来,我们需要输入查询条件,点击搜索按钮,所以我们告诉capybara去让driver在页面上查找搜输入栏,并填写搜索条件。

1
2
3
fill_in 'kw', :with => search_term
click_on 'su'

这里唯一的困惑是fill_in 方法中使用’kw’这个关键字,当你代码告诉capybara去在页面上寻找
这个关键字时,capybara使用了一种’猜测’的机制。
也就是说capybra试图在DOM元素各种属性中找到你要求的。在这个例子中,我们知道baidu搜索表栏的id属性kw,这是我们需要在代码中提供的。
最后,我们需要检查返回的搜索结果是否正确。为此,我们使用capybara的内置RSpec匹配器。如果你不知道RSpec,rspec官方文档是你最好的选择(http://rspec.info/),与传统断言不同是当条件不满足时,会抛出异常,而不是只是返回false

1
page.should have_content 'capybara'

最后需要注意的是,have_content方法的匹配器内置了一个默认的等待。这是非常有用的,因为如果查找的内容我是通过JavaScript异步加载而成的话(而不是在初始页面就加载完成),capybara在我们设置默认等待时间重试,看它是否存在。我们将在后面的章节介绍如何处理异步JavaScript的问题。

总结,本章目标是让你认识capybra,以及配置它的运行环境,ruby和rubygems以及相关依赖。最后,我们通过一个简单的场景来编写自动化,来加强我们的学习。