Showing posts with label capybara. Show all posts
Showing posts with label capybara. Show all posts

Monday, October 26, 2015

Debugging Capybara Poltergeist tests in Rails

I made the switch from selenium webdriver to poltergeist on my capybara tests when the suite started taking 20 minutes to run. (For my process of getting the test suite under control, check out my previous post.) While poltergeist has definitely been a boon, it can sometimes be hard to debug because I can’t physically see the page. That’s where this tip comes in handy, stolen from the Quickleft blog:

Find the Port Address and Pause the Test 
Sometimes I just want to see what's going on in the browser. Although save_and_open_page gives you some idea of what's going on, you can't really click around, because the page it gives you is static and lacking assets. To dig into what's going on, I like to use a trick I learned from my mentor, Mike Pack. 
Just above the broken line in your test, add these two lines of code. Note that you have to have pry installed in your current gemset or specified in the Gemfile for this to work. 
puts current_url
require 'pry'; binding.pry 
Run the specs, and when they pause, copy the url and port number from the test output. 
Open your browser and paste the address into the window. Voila! You're now browsing your site in test mode!

Thanks again to the Quickleft folks for this cool tip - it’s one of five tricks for less painful testing.

Wednesday, June 24, 2015

Take control of your rspec test suite, Part 1

Let me tell you a little story about how I took our test suite from 25 minutes run time to just under 3 minutes while still testing all the same functionality.

A large application can generate a very unwieldy test suite. On one project I worked on, the test suite took 45 minutes to run on our CI, and when I tried to run the whole thing locally, I stopped it at the 1 1/2 hour mark when it looked like it still had a ways to go.

I can live with a suite like that for awhile, because I just run the tests that are specific to the work I’m doing. However, there are times when I’m working on something that could potentially affect several different places in the app, and I may not know exactly where those places are, so I have to run the whole suite. Waiting 45 minutes to be able to finish my feature is NOT cool, especially since that will likely have to happened a few times to ensure all tests are fixed.

On that project, I was limited with what I could do on the test suite due to budget constraints. However, on another project I worked on, our test suite took 25 minutes to run, but the project manager saw the financial benefit of trimming this number down and so let me spend some time fixing our tests.

I’ll be writing a few different posts on this topic. Step 1 is the easiest part: gathering data. Often, only a handful of tests take the longest to run, so we want to find out which of our tests are “losers” and try to refactor them into “winners.”

Thankfully, rspec gives us a command to do just that:

rspec --profile
# or rspec -p

This will run the entire suite, then list out the 10 examples that took the longest to run. If you want more than 10, you can specify the number you want as an argument, i.e. rspec -p 25.

You can also use this when running on certain test groups. So, if you wanted to find out the 17 slowest feature specs:

rspec -p 17 spec/features

Now you know the worst offenders. Stay tuned for part 2 where we delve into how to fix them up.

Thursday, June 18, 2015

Hack your Rspec setup using tags

I ran into an issue in rspec the other day where I had a before block to setup about 10 different tests, but I needed to write one more that was similar enough that it belonged in the same context but it required slightly different setup so the before block I already defined.

What to do? I could break out the existing tests into a nested context block and use the setup there, then do another nexted context block for my new test, but I really didn’t want to do that because all these tests should belong in the same context. I just wanted to skip the before block on one specific test.

Rspec tags to the rescue!

In this case, my new test was a JS test and all others were not (which is partly why the setup was different. So, the test was already tagged js: true, and in mybefore` block, I modified it like so:

before(:each) do |test|
  # Run this only on tests NOT tagged with js: true
  if test.example.metadata[:js] == nil
    # Existing setup code
  end
end

Works like a charm, and I still have all my related tests within the same context like I wanted.

Hat Tip: http://stackoverflow.com/questions/27864080/how-can-i-skip-some-setup-for-specific-rspec-tags

Wednesday, May 6, 2015

Testing CSV exports/downloads in Ruby on Rails with Capybara

As low-tech as CSV is, it doesn't appear to be going away anytime soon. So, I've run into a few different Ruby on Rails projects that want import from/export to CSV capability, and because I like tests, I wanted to make sure I could test not only that the CSV is exported but that the content of the CSV is correct.

Here's how to do this using Capybara and Rspec:

First, switch the driver to something that works (webkit or rack_test, but not selenium). To do this, you can run this within your code block:

Capybara.javascript_driver = :webkit

Just make sure to change it back to your preferred driver after you're done. Another option is to do this in your spec_helper.rb:

config.before(:each, webkit: true) do
    Capybara.javascript_driver = :webkit
end

config.after(:each, webkit: true) do
    Capybara.javascript_driver = :selenium 
    # or whatever your preferred driver is
end

Then just tag any CSV tests with the webkit: true tag.
(Thanks to Kevin Bedell on StackOverflow for this one.)

Once you get the driver configured properly, you can check the headers and the actual content of the CSV trivially:

click_on 'Download as CSV'
# Make sure the page is a CSV
header = page.response_headers['Content-Disposition']
expect(header).to match /^attachment/
expect(header).to match /filename="my_file.csv"$/

# Then check the content
MyModel.all.each do |record|
  expect(page).to have_content record.field1
  expect(page).to have_content record.field2
end

(Props to Andrew on StackOverflow for this bit.)

Saturday, March 28, 2015

Capybara tests with Rails `disable_with` on forms

Here's something that threw me for a loop for a little bit.

When using the disable_with option on form submit buttons, under the hood this is running some minimal JavaScript code, so Capybara tests have to be run as js: true, otherwise they will fail.

Wednesday, January 7, 2015

Reload stale objects in finnicky Rails rspec tests

I have had plenty of times where an rspec test was failing because an object was still showing an old value after an update. This seems to happen most often in Capybara. I know I'm not technically supposed to be checking object data in Capybara tests, just checking what's actually seen on the page, but there are some times where I need to be extra sure, or where the data being updated isn't showing on the page.

In rspec, call #reload on an object if it has changed in the database in order to sync and get the most recent changes.

NOTE: The object must have an ID, because under the hood it is just running
object = Object.find(object.id)

object.name # => 'Frank'
object.update(name: 'Tank')
object.name # => 'Frank'
object.reload
object.name # => 'Tank'