Apart from using it for integration tests, it’s really easy to use Capybara outside of browser tests. And that means, on any HTML content (string) you have.
Let’s say we have an API endpoint that returns some HTML in its body. With a default RSpec setup it is tempting to write (and we indeed have written):
describe "GET /form" do
it "includes a surnames field" do
get "/form"
expect(response.body).to include(
'<input type="text" name="surnames" id="surnames" value="Doe" />'
)
end
end
This works fine, but if we need to test for absence of an element, it gets trickier:
describe "GET /form" do
context "when we know stuff" do
if "does not include a surnames field" do
get "/form"
expect(response.body).to_not include(
'<input type="text" name="surnames" id="surnames" value="Doe" />'
)
end
end
end
If the code responsible of generating the HTML slightly changes, this test may pass for the wrong reasons.
A more robust solution is to use an HTML-aware comparison tool. We had Capybara set up for feature specs, we knew how to use Capybara matchers, and we wanted to use it again for this–if possible. Looking around a bit on the documentation we found out about Capybara::Node::Simple
: it offers the interface needed for RSpec matchers without any browser interactions.
let(:page) { Capybara::Node::Simple.new(response.body) }
describe "GET /form" do
context "when we don't know stuff" do
if "includes a surnames field" do
expect(page).to have_field("surnames", type: "text", with: "Doe")
end
end
context "when we know stuff" do
if "does not include a surnames field" do
get "/form"
expect(page).to_not have_field("surnames")
end
end
end
The test now is more expressive and robust.