Rspec-page-regression is an RSpec plugin that makes it easy to headlessly regression test your web application pages to make sure the pages continue to look the way you expect them to look, taking into account HTML, CSS, and JavaScript.
It provides an RSpec matcher that compares the test snapshot to an expected image, and facilitates management of the images.
Rspec-page-regression uses PhantomJS to headlessly render web page snapshots, by virtue of the Poltergeist driver for Capybara. You can also use the Selenium driver to test against real browsers.
Rspec-page-regression is tested on ruby 1.9.3, 2.1.0, and jruby
Install PhantomJS as per PhantomJS: Download and Install and/or Poltergeist: Installing PhantomJS. There are no other external dependencies (no need for Qt, nor an X server, nor ImageMagick, etc.)
In your Gemfile:
gem 'rspec-page-regression'
And in your spec_helper:
require 'rspec' # or 'rspec/rails' if you're using Rails
require 'rspec/page-regression'
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Rspec-page-regression presupposes the convention that your spec files are somwhere under a directory named spec
(checked in to your repo), which has a sibling directory tmp
(.gitignore'd)
To install for use with Selenium, see instructions below.
Rspec-page-regression has multiple versions that work in concert with the significant changes in RSpec version 3. If you're using bundler, the gem dependencies should automatically find the proper version of rspec-page-regression for your chosen version of RSpec.
Rspec Version | Rspec-page-regression |
---|---|
>= 3.0.* | >= 0.3.0 |
2.99 | 0.2.99 |
<= 2.14.* | <= 0.2.1 |
Rspec-page-regression provides a matcher that renders the page and compares
the resulting image against an expected image. To use it, you need to enable Capybara and Poltergeist by specifying :type => :feature
and :js => true
:
require 'spec_helper'
describe "my page", :type => :feature, :js => true do
before(:each) do
visit my_page_path
end
it { expect(page).to match_expectation }
context "popup help" do
before(:each) do
click_button "Help"
end
it { expect(page).to match_expectation }
end
end
The spec will pass if the test rendered image contains the exact same pixel values as the expectated image. Otherwise it will fail with an error message along the lines of:
Test image does not match expected image
$ open tmp/spec/expectation/my_page/popup_help/test.png spec/expectation/my_page/popup_help/expected.png tmp/spec/expectation/my_page/popup_help/difference.png
Notice that the second line gives a command you can copy and paste in order to visually compare the test and expected images.
It also shows a "difference image" in which each pixel contains the per-channel absolute difference between the test and expected images. That is, the difference images is black everywhere except has some funky colored pixels where the test and expected images differ. To help you locate those, it also has a red bounding box drawn around the region with differences.
The easiest way to create an expectation image is to run the test for the first time and let it fail. You'll then get a failure message like:
Missing expectation image spec/expectation/my_page/popup_help/expected.png
$ open tmp/spec/expectation/my_page/popup_help/test.png
To create it:
$ mkdir -p spec/expectation/my_page/popup_help && cp tmp/spec/expectation/my_page/popup_help/test.png spec/expectation/my_page/popup_help/expected.png
First view the test image to make sure it really is what you expect. Then copy and paste the last line to install it as the expected image. (And then of course commit the expected image into your repository.)
If you've deliberatly changed something that affects the look of your web page, your regression test will fail. The "test" image will contain the new look, and the "expected" image will contain the old.
Once you've visually checked the test image to make sure it's really what you want, then simply copy the test image over the old expectation image. (And then of course commit it it into your repository.)
The failure message doesn't include a ready-to-copy-and-paste cp
command, but you can copy and paste the individual file paths from the message. (The reason not to have a ready-to-copy-and-paste command is if the failure is real, it shouldn't be too easy to mindlessly copy and paste to make it go away.)
As per the above examples, the expectation images default to being stored under spec/expectation
, with the remainder of the path constructed from the example group descriptions. (If the it
also has a description it will be used as well.)
If that default scheme doesn't suit you, you can pass a path to where the expectation image should be found:
expect(page).to match_expectation "/path/to/my/file.png"
Everything will work normally, and the failure messages will refer to your path.
The default window size for the renders is 1024 x 768 pixels. You can specify another size as [height, width]
in pixels:
# in spec_helper.rb:
RSpec::PageRegression.configure do |config|
config.page_size = [1280, 1024]
end
Note that this specifies the size of the browser window viewport; but rspec-page-regression requests a render of the full page, which might extend beyond the window. So the rendered file dimensions may be larger than this configuration value.
By default, a test fails if only a single pixel in the screenshot differs from the expectation image. To account for minor rendering differences, you can set a threshold value that allows a certain amount of differences. The threshold value is the fraction of pixels that are allowed to differ before the test fails.
RSpec::PageRegression.configure do |config|
config.threshold = 0.01
end
This setting means that 1% of pixels are allowed to differ between the rendering result and the expectation image. For example, for an image size of 1024 x 768 and a threshold of 0.01, the maximum number of pixel differences between the images is 7864.
You can also use the selenium driver with capybara. This offers the possiblity to visually test your pages against a range of real browsers.
Add the selenium-webdriver to your Gemfile:
gem 'selenium-webdriver'
And in your spec_helper replace:
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
With:
require 'selenium/webdriver'
Capybara.javascript_driver = :selenium
See also the capybara readme and selenium wiki for more information.
Contributions are welcome! As usual, here's the drill:
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Don't forget to include specs (rake spec
) to verify your functionality. Code coverage should be 100%
Release Notes:
-
0.4.2 - Now works with jruby. Thanks to @paresharma
-
0.4.1 - Bug fix: wasn't including example name in file path. Thanks to @kurtisnelson
-
0.4.0 - Add difference threshold. Thanks to @abersager
-
0.3.0 - Compatibility with rspec 3.0
-
0.2.99 - Compatibility with rspec 2.99
-
0.2.1 - Explicit dependency on rspec ~> 2.14.0
-
0.2.0 - Support selenium. Thanks to @edwinvdgraaf
-
0.1.2 - Remove existing difference images so they won't be shown in cases where files couldn't be differenced.