TL;DR
Running acceptance tests against a full web stack, like PhantomJS, can decrease overall testing time by reducing the number dependencies (i.e. Xvfb) needed by your CI server. This post will go over how we package and distribute PhantomJS and how to write acceptance tests to catch JavaScript errors before they hit production.
Benefits of Acceptance Testing
Acceptance testing is key to ensuring your browser-facing apps are working as expected. With acceptance testing you can write assertions on what users should expect to see on a page by testing that the required DOM elements are loaded. By loading a full DOM environment in your testing framework, these tests can catch JavaScript errors on your pages before they go out to production.
Acceptance Testing Framework - Capybara/Poltergeist
As useful as these types of tests can be, getting acceptance tests running on a CI server can be tricky and can add significant overhead to testing time. We use Capybara to write acceptance tests because of its intuitive DSL, integration with Rails, and the ability to integrate with different web-drivers. We use Poltergeist as the web-driver for Capybara because it allows us to use PhantomJS on our CI server which doesn’t require an X server to run tests against a browser. Poltergeist is solely dependent on PhantomJS and will require any machine running your acceptance tests to have PhantomJS available.
Providing the PhantomJS Dependency
Before we can start using a headless browser in our acceptance testing framework, we’ll first need to provide the PhantomJS dependency for our system. Since there are no official packages for PhantomJS, we need to build it from source on our target operating system, and then package it into the format we need. While there are no official packages (deb, rpm, etc.) for PhantomJS, there is an official static build available on the PhantomJS website. We also have a packagecloud repository containing PhantomJS packages for different operating systems. We won’t go over the steps on how to build the package from source, but you can take a look at this post and this post to get a better idea on how to build packages.
Once we have the package for PhantomJS, we can push it up to our packagecloud repository.
package_cloud push armando/phantomjs ./phantomjs_2.0.0_amd64.deb
Now all we have to do is install the repository onto our CI server using the bash scripts provided by packagecloud.
curl -s https://packagecloud.io/install/repositories/armando/phantomjs/script.deb.sh | sudo bash
And install the package during the CI setup process:
sudo apt-get install phantomjs
Testing for JavaScript errors using Capybara with Poltergeist and Rspec
Capybara uses rack_test
as its default driver for running tests, which is fast but doesn’t support JavaScript. To test for JavaScript errors in your Capybara tests you can enable it in the scenario
(which is an alias for it
in the Capybara DSL):
feature "Some JavaScript Stuff" do
scenario "load homepage", :js => true do
visit "/"
expect(page).to have_content "some content enabled by javascript"
end
end
or
@javascript
feature "Some JavaScript Stuff" do
scenario "load homepage" do
visit "/"
expect(page).to have_content "some content enabled by javascript"
end
end
By default, Capybara uses selenium
as its JavaScript driver when defining tests with :js => true
or @javascript
. This means the test above will use the selenium
web-driver without any further configuration. To override the default driver to use Poltergeist, set the following value in your spec helper or wherever you configure your tests:
Capybara.javascript_driver = :poltergeist
With this setting, any tests that are specified to be run with JavaScript enabled will use Poltergeist as the web-driver.
Conclusion
Writing acceptance tests is a crucial part of the development process. Modern web applications often require the loading and manipulation of elements into a DOM, so enabling a full DOM environment when running your acceptance tests is critical to catching potential user facing bugs before they hit production. Using a headless browser like PhantomJS enables us to test end-to-end features without bogging down our testing pipeline with extraneous dependencies.