Getting this setup working took me an hour of searching the web to get some of the interactions working, so I hope this helps you do it faster and avoid some pitfalls I stumbled into.
This post describes some of the obstacles I ran into getting proper JS-supported system tests working in my up-to-date Rails app, Infinity Feed.
It used to be complex to get Capybara working with a JS-enabled headless browser for your integration tests. This has gotten significantly easier with Rails 5.x and got another boost with 6.x.
If your knowledge about Rails frontend testing still stems from the Rails v4 or v5 era, you might be pleasantly surprised by how easy it can be now.
Setting the scene
For this blog post, the relevant bits of my stack are:
- capybara 3.35
- faraday 1.4
- rails 6.1
- rspec 3.10
- rspec-rails 5.0
- turbo-rails 0.5
- vcr 6.0
- webdrivers 4.6
- webmock 3.13
You’ve got the usual suspects here that test Rails with RSpec. I use unit tests for models and other plain old Ruby objects (POROs), there are controller tests for specific interactions and there was a feature test intended to test my frontend interactions. This is done differently in the modern era by using system tests.
I use Faraday for HTTP calls, so VCR + Webmock is part of my test stack to intercept & record HTTP calls so they can be replayed without hitting the network during my tests. This makes tests more consistent and faster. Win-win!
I had a feature test written with out-of-the-box Capybara which appeared to work just fine, until I wanted to test that Hotwire Turbo was doing its magic to make automatic JS-powered HTTP calls to replace my HTML with new HTML. The JS did not get executed in my tests!
So, I had to figure out how to enable JS. Remembering how much of an ordeal this used to be, and how often the best practices changed, I started searching the internet for how it is done in 2021.
System tests have replaced features
Since Rails 5.0/5.1 there are system tests, which are similar to the old feature tests we had, but now Rails handles all of the overhead you used to have to do yourself. You can stop futzing with DatabaseCleaner and configuring Puma to run in-process. It’s all taken care of by Rails now.
Since Rails 6, integration with browsers for testing happens via the webdrivers project, which handles downloading and updating the browser for you. It just works! Just beware of unexpected VCR interactions (see below).
Migrating features to system tests
It’s really easy:
- Move feature files from
type: :systemif needed for these files
To the top of your system test files to pick a driver that supports JS. You can use this to configure different browsers and screen sizes.
And now you have working system tests!
There’s a few extra things left to configure if you need them.
If you use Devise to handle your authentication, you should register its integration test helpers to be used in feature and system tests:
1 2 3 4 5 6 7
This enables the very useful
sign_in helper function, so your tests can focus on testing actual features instead of always having to simulate a login.
VCR helps you intercept HTTP calls during tests, as I mentioned earlier. The thing is that Webdrivers automatically checks if the latest version of the browser is installed and downloads it if needed. As you can imagine, this did not play well with VCR.
My solution is to force the webdriver to check for an update before I configure VCR. VCR also needs to be told not to interfere with system test calls.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
This worked for me ™, so I hope it helps you as well. If you run into issues, feel free to reach out to me via Twitter or email. Besides being close to launching a smart RSS reader over at Infinity Feed, I’m a freelance Ruby software developer with a focus on back-end systems and an obsession with code quality.