0

I'm using Capybara and Poltergeist to test an index page that uses setTimeout to periodically refresh page content. The page content transitions between three states, based on an external service, and I've got a cassette for each of these states.

I'm trying to test the refresh using code like this:

VCR.use_cassette 'object_new' do
  visit index_path
  page.should have_content 'New'
end

VCR.use_cassette 'object_running' do
  page.should have_content 'Running'
end

VCR.use_cassette 'object_complete' do
  page.should have_content 'Complete'
end

The first have_content succeeds, but the second does not. Capybara should be waiting for any async calls to return and update the DOM so that these assertions pass. I've tried using_wait_time to force the assertion to wait long after the ajax call returns, but it doesn't help. Any ideas as to what might be missing from my spec to make this work? The functionality does work in the browser; it's just the tests that aren't passing.

2 Answers2

2

When you use the poltergeist driver to run your tests, your pages are actually being rendered in a separate process (i.e. a phantomjs process).

This means if your javascript loads any external content, PhantomJS will actually load the external content rather than loading the VCR cassette, as VCR only affects HTTP calls made within the Ruby process running your tests.

Rather than trying to test your JS code with Capybara, you should consider testing your JS with something like Jasmine, where you will have the chance to mock out the calls to the external service.

lmars
  • 2,502
  • 1
  • 16
  • 9
  • The calls to the external service are being made from the Rails app, and the JS is accessing a RESTful API. I'd like to test the full stack, which is why I'd rather use the Rails app's API and mock the service at the back-end using VCR. That's where the problem lies; when the JS calls the API, based on the spec, it should have changed cassettes (since the JS is calling a test version of my Rails app that's running), but I'm still getting the response as if the first cassette was loaded. – jmacdonald Aug 27 '13 at 02:11
  • How are you co-ordinating the AJAX calls from JS code with the changing of the cassette? It is likely that the AJAX calls are happening after / before you have changed the cassette because they are happening in a separate process. It may be worth adding some timestamp logging to your cassette changes and controllers to see when requests are being made. – lmars Aug 27 '13 at 10:32
  • 1
    Looking at the AJAX responses, they're reporting a 500 error, which is why the tests are failing. Looking at the test logs doesn't reveal any other detail. The AJAX bit keeps retrying every 5 seconds, so even if it doesn't catch the right response on the first try, it eventually will. I don't think the cassette switching is causing the error, because VCR is usually very vocal when a request is made without a cassette. The first page.should assertion does pass, for what it's worth. – jmacdonald Aug 27 '13 at 21:56
0

The initial implementation is actually correct, but my JS wasn't resilient enough to handle server errors. Requests were being fired between cassette reloads, and were returning non-success HTTP statuses.

Updating the JS to handle erroneous requests and continuing polling solved this issue, and in the end, is the how the JS should have been behaving anyway.