I came across a problem that I’ve had with Capybara testing that has come up for me before.
In my test, I needed to check that an element was not on screen. I was trying to use Capybara’s #find method combined with RSpec exist matchers to keep the tests fast.
admin_section = find("#admin") expect(admin_section).not_to be_present
However, you can’t use Capybara’s #find method here in this case, because it will throw an error (ironically, that it can’t find the element, thus, returning nil) before the matcher can be evaluated. Capybara will also wait 2 seconds before finally throwing the error which will actually slow down the test.
I Googled for the answer and while this Stack Overflow answer does provide the basic answer, it doesn’t use the new “expect” syntax. Here is how to check that an element does not exist on-screen using the expect syntax.
I don’t test much for elements that are not on-screen. But, for certain, exceptional cases, I will test when I want to make sure that an element is not appearing on-screen (say, a link to the admin section when you are a typical user).
However, I rarely test solely that an element does not exist on screen. The reason for this is that there are too many ways that an element could not appear on-screen. For example, maybe normally a test needs to be logged-in for the scenario under test but for some reason, my tests are logging out early. Now, my “not present” test may still pass because the element still does not exist on-screen but that’s because I’m logged out.
To account for this, I usually include an assertion for a similar element nearby that should be visible and is related in behavior to the element that is supposed to be hidden.
expect(page).to have_selector("#profile-photo") expect(page).not_to have_selector("#admin")
If you are curious, ThoughtBot’s Joe Ferris hits on the points about using RSpec matchers like “have_selector” over Capybara’s #find method in this post on asynchronous testing.