Integration Testing

Learning Integration Testing

Integration Testing

Let’s learn Integration Testing. Integration tests are critically important because they exercise your application just like a real user. Therefore, depending on the full stack from your models up through your controllers, helpers, view templates, web server, database, and middleware.

Integration tests can be brittle if they know too much about how those components work. Proper integration tests use your application as a black box. They should know as little as possible about what happens under the hood, they’re just there to interact with the interface and observe the results.

rack-test: Integration Testing

First, a little background.

Integration tests in Rails applications work by simulating a user’s experience through the HTML interface. You load up your Rails application, and your integration test harness (such as Capybara) simulates a user clicking around on the site.

Without JavaScript, Capybara uses rack-test as a driver by default and looks something like this:

  • Firstly, You trigger user action by invoking one of Capybara’s DSL methods, such as visit.
  • Secondly, Capybara tells its driver (rack-test) to load the requested URL.
  • Thirdly, The Rack response is parsed by rack-test and saved as the current page.

Selenium, capybara-webkit, and Poltergeist: Integration Testing

JavaScript drivers work a little differently. Because browser tools like WebKit are difficult to load inside of a Ruby process, these drivers boot up an external process which can interact with the browser engine. Because the external process doesn’t have access to the in-memory instance of your Rails application, they must make actual HTTP requests.

Interactions performed using Capybara’s JavaScript drivers look something like this:

  • You trigger user action by invoking one of Capybara’s DSL methods, such as visit.
  • Capybara tells its driver (such as capybara-webkit) to load the requested URL.
  • The driver starts its external process to hold the browser engine.
  • In order to serve actual HTTP requests, Capybara boots a new instance of your Rails application in a background thread.

Capybara to the Rescue: Integration Testing

Much of Capybara’s source code is dedicated to battling this asynchronous problem. Capybara is smart enough to understand that a page may not have loaded by the time you try to interact with it. A typical interaction might look like this:

Test Thread Application Thread
Your test invokes visit. Waiting for a request.
Capybara tells the driver to load the page. Waiting for a request.
The driver performs a request. Waiting for a request.
Your test invokes click_link. Your application receives the request.
Capybara looks for the link on the page, but it isn’t there. Your application sends a response.
Capybara tries to find the element again, but it’s not there. The driver receives the response.
Capybara successfully finds the element from the response. Waiting for a request.

As you can see, Capybara handles this interaction gracefully, even though the test starts looking for a link to click on before the page has finished loading.

However, if Capybara handles these asynchronous issues for you, why is it so easy to write flapping tests with Capybara, where sometimes the tests pass and sometimes they fail?

There are a few tricks to properly using the Capybara API so as to minimize the number of possible race conditions.

Find the first matching element

Bad:

first(“.active”).click

If there isn’t an .active element on the page yet, first will return nil and the click will fail.

Good:

# If you want to make sure there’s exactly one

find(“.active”).click

# If you just want the first element

find(“.active”, match: :first).click

Capybara will wait for the element to appear before trying to click. Note that match: :first is more brittle, because it will silently click on a different element if you introduce new elements which match.

Interact with all matching elements

Bad:

all(“.active”).each(&:click)

If there are no matching elements yet, an empty array will be returned, and no elements will be affected.

Good:

find(“.active”, match: :first)

all(“.active”).each(&:click)

Capybara will wait for the first matching element before trying to click on the rest.

Directly interacting with JavaScript

Bad:

execute_script(“$(‘.active’).focus()”)

JavaScript expressions may be evaluated before the action is complete, and the wrong element or no element may be affected.

Good:

find(“.active”)

execute_script(“$(‘.active’).focus()”)

Checking a field’s value

Bad:

expect(find_field(“Username”).value).to eq(“Joe”)

Capybara will wait for the matching element and then immediately return its value. If the value changes from a page load or Ajax request, it will be too late.

Good:

expect(page).to have_field(“Username”, with: “Joe”)

Capybara will wait for a matching element and then wait until its value matches, up to two seconds.

Checking an element’s attribute

Bad:

expect(find(“.user”)[“data-name”]).to eq(“Joe”)

Capybara will wait for the matching element and then immediately return the requested attribute.

Good:

expect(page).to have_css(“.user[data-name=’Joe’]”)

Capybara will wait for the element to appear and have the correct attribute.

Looking for matching CSS

Bad:

it “doesn’t have an active class name” do

expect(has_active_class).to be_false

end

 

def has_active_class

has_css?(“.active”)

end

Capybara will immediately return true if the element hasn’t been removed from the page yet, causing the test to fail. It will also wait two seconds before returning false, meaning the test will be slow when it passes.

Good:

it “doesn’t have an active class name” do

expect(page).not_to have_active_class

end

 

def have_active_class

have_css(“.active”)

end

Capybara will wait up to two seconds for the element to disappear before failing, and will pass immediately when the element isn’t on the page as expected.

Make your resume stand out and become a Certified Capybara Testing Professional. Try free practice tests here!

A great career is just a certification away. So, practice and validate your skills to become a Certified Capybara Testing Professional.

Beware the XPath // trap
JavaScript Testing

Get industry recognized certification – Contact us

keyboard_arrow_up