Knowing how to use testing frameworks is one of the most important skills any developer must learn. In particular with the Ruby programming language, testing is even more important because a compiler will not catch syntax errors or little slip ups! Such is life when using interpreted languages. Despite testing’s importance being obvious, it is surprising how many Ruby on Rails apps have deficient test coverage or no code coverage at all! Why is this?
I think one of the simplest explanations is that it is hard. I see discussion board’s like this one on Reddit where Rails practitioners are struggling to get full stack testing to work. Apart from the difficulty of even getting some test frameworks to work, it takes discipline to spend additional time writing tests for code instead of merely committing and shipping. Add these two factors together and I can see why so many developers just give up and move on.
With that said, I wanted to share my own Selenium/RSpec configuration. I use this configuration on all of my own personal Ruby on Rails projects. While it may not be perfect for you or your setup, it works well enough for us.
First is my Gemfile:
group :test do gem 'rspec-rails', '~> 4.0.0' gem 'capybara' gem 'webdrivers', '~> 4.0' gem 'selenium-webdriver' gem 'database_cleaner' gem 'launchy' # `save_and_open_page` in integration specs end
Second is my Capybara Configuration:
Whats going on in this file? Quite a bit and it took me a lot of trial and error to settle on this particular combination of settings.
First, this file sets up TWO capybara drivers.
chrome_headless is useful for everyday testing and CI servers. It’s really annoying to have a Chrome window popup while you’re trying to work. Capybara will steal focus too so its not possible to merely hit or minimize the window easily either. This mode is useful on CI server’s too because those system’s usually don’t have anyone watching the tests run on them! The other driver,
chrome, will open up a browser window and I can watch the tests run. I primarily use this for debugging and for writing new tests! In practice, if I wanted to run a test in a browser window and see whats happening I would merely prepend
Second, Selenium’s Capabilities class is where things can get a touch confusing. When using
chrome_headless, we have to pass options to chrome so it knows what mode to be in! The documentation for it is here but the options I’ve used above will do the trick.
--window-size is one that really helped me out because it stops Capybara from not being able to see elements that may be too far down the screen. This opens up a pretty big browser window so it hopefully minimizes screen size related issues.
Third, if you have VCR or another gem that prevents leaking HTTP requests, you have to allow Webmock to download the chromedriver. This is mainly needed on CI systems, but you may need it in your local test environment also. If you have
chromedriver installed already, you may not need it at all. This is one of those environmental configuration lines that could easily not apply to you - but I included it anyways just in case.
Lastly, controlling tests while they are running can be difficult too. I use the following Macro’s to help!
#spec/support/capybara_macros.rb module CapybaraMacros def scroll_to_bottom page.execute_script 'window.scrollBy(0,10000)' end def wait_for_ajax Timeout.timeout(Capybara.default_max_wait_time) do loop until finished_all_ajax_requests? end end def finished_all_ajax_requests? page.evaluate_script('jQuery.active').zero? end #https://ricostacruz.com/til/pausing-capybara-selenium def pause_selenium #not to be confused with Kernel#pause $stderr.write 'Press enter to continue' $stdin.gets end # hack to make element visible on page def maximize_browser_window Capybara.current_session.current_window.resize_to(1000, 1000) end end RSpec.configure do |config| config.include CapybaraMacros, type: :feature config.include Warden::Test::Helpers end
Let’s take a look at them one by one:
scroll_to_bottom- scrolls the browser window down to what is most likely the bottom. 10000 is a lot! Sometimes you need to test something at the very bottom of the page. This macro shows you how.
wait_for_ajax- this one saves my skin all the time. Use this after opening up a modal popup or anytime there’s some kind of transition. This merely tells Capybara to chill out while the browser finishes doing stuff. This works with jQuery and I’m not sure about other frontend frameworks. Odds are for Angular, Vue, etc, you’d just have to adjust the
finished_all_ajax_requests?method to work with your particular setup!
pause_selenium- the equivalent of
binding.prywhile running a JS-enabled feature spec. This will pause the Ruby thread completely allowing you to pause your feature spec right where you want so you can debug. Do not commit this - it will freeze your CI server as it requires keyboard input to exit from.
maximize_browser_window- helps enlarge the browser window in case the size gets messed up for some reason.
save_and_open_page- This one is provided by the Launchy gem, but its a nice one to have if you want to save the current state and open it on a separate browser window.
Lastly, we need to configure Database Cleaner so RSpec doesn’t leave behind junk in your database after a JS-enabled Spec has completed. Without the following configuration, it’s possible your test suite or CI server might error randomly.
RSpec.configure do |config| config.before(:suite) do DatabaseCleaner.clean_with(:truncation) end config.before(:each) do DatabaseCleaner.strategy = :transaction end config.before(:each, type: :feature) do DatabaseCleaner.strategy = :truncation end config.before(:each) do DatabaseCleaner.start end config.append_after(:each) do DatabaseCleaner.clean end end
TL;DR - Too Long Didn’t Read
Testing is tough as-is, and the wrong settings can make it more difficult than it ought to be. The above settings work on all of our Ruby on Rails projects. Feel free to adapt to your projects!