Questions
1. what is
A headless browser is a web browser without a graphical user interface. They are particularly useful for testing web pages as they are able to render and understand HTML the same way a browser would, including styling elements
2. what is
Capybara is a web-based automation framework used for creating functional tests that simulate how users would interact with your application.
2. what is
Without JavaScript, Capybara uses rack-test as a driver by default and looks something like this:
visit
.
visit
.
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. |
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
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)
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()")
Capybara will wait until a matching element is on the page, and then dispatch a JavaScript command which interacts with it.
Note: execute_script
should only be used as a last resort when running into driver limitations or other issues which make it impossible to use other Capybara methods.
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.
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.
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.
When interacting with the page, use action methods like click_on
instead of finder methodslike find
whenever possible. Capybara knows the most about what you’re doing with those methods, and can more intelligently handle odd edge cases.
When verifying that elements are on the page as expected, use RSpec matchers likehave_css
instead of node methods like text
whenever possible. Capybara can wait for the result you want with a matcher, but doesn’t know what text you’re expecting when you invoke methods on a node.
references
https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara