{"id":2804,"date":"2012-06-12T10:25:39","date_gmt":"2012-06-12T13:25:39","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=2804"},"modified":"2012-06-12T10:25:39","modified_gmt":"2012-06-12T13:25:39","slug":"improving-the-integration-between-capybara-and-rspec","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2012\/06\/improving-the-integration-between-capybara-and-rspec\/","title":{"rendered":"Improving the integration between Capybara and RSpec"},"content":{"rendered":"

When David Chelimsky was visiting S\u00e3o Paulo in last April, we invited him to go out for some coffee, beers and brazilian appetizers. We had a great time and we talked about different topics like OO, programming languages, authoring books and, as expected, about testing.<\/p>\n

One of the topics in our testing discussion was the current confusion in rspec-rails request specs when using Capybara. There is an open issue for this in rspec-rails’ issues tracker<\/a> and discussing it personally allowed us to talk about some possible solutions, which could take months in internet time. \ud83d\ude42<\/p>\n

rspec-rails is a gem that wraps Rails testing behaviors into RSpec’s example groups. For example, the controller example group is based on ActionController::TestCase::Behavior<\/code><\/a>. There are also example groups for views, helpers and so forth, but for now we are interested in the request example group, which is as a wrapper for ActionDispatch::Integration::Runner<\/code><\/a>. The Rails’ integration runner is built on top of rack-test<\/code>, a great small gem that adds support to methods like get<\/code>, post<\/code>, put<\/code> and delete<\/code> and handle the rack request and response objects.<\/p>\n

This setup with the request example group running on top of Rails’ Integration Runner works fine until you add Capybara to your application (which is always a good idea). The issue is that Capybara by default includes its DSL in the same request example group<\/a> and that’s when the confusion starts.<\/p>\n

Capybara, being an acceptance test framework, does not expose low-level details like a request or response object. In order to access a web page using Capybara, the developer needs to use the method visit<\/code> (instead of get<\/code>). To read the accessed page body, the developer must use page<\/code> instead of manipulating the response<\/code>.<\/p>\n

However, since both Capybara DSL and Rails’ Integration Runner are included in the same example group, both methods visit<\/code> and get<\/code> are available! Not only that, even if I visit a web page using Capybara’s visit<\/code>, I can still access the request and response object that comes from Rails, except that they will be blank since Capybara uses a completely different stack to access the application.<\/p>\n

This confusion not only happens inside each test<\/a> but it also leads to a poor testing suite. I have seen many, many files inside spec\/requests<\/code> that mixes both syntaxes.<\/p>\n

Talking to David, I have expressed a possible solution to this problem based on how we have been building applications at Plataformatec. First of all, we start by having two directories: spec\/requests<\/code> and spec\/acceptance<\/code>. Since both are supported by Capybara, this (mostly) works out of the box.<\/p>\n

Everything you want to test from the user\/browser perspective goes under spec\/acceptance<\/code>. So if you want to test that by filling the body and the title fields and pressing the button “Publish” publishes a new blog post, you will test that under acceptance (protip: we usually have subdirectories inside spec\/acceptance<\/code> based on the application roles, like spec\/acceptance\/guest<\/code>, spec\/acceptance\/admin<\/code>, etc).<\/p>\n

Everything under spec\/requests<\/code> applies to the inner working of your application. Is it returning the proper http headers? Is this route streaming the correct JSON response? Also, since APIs are not part of the user\/browser perspective, they are also tested under spec\/requests<\/code> and not under spec\/acceptance<\/code><\/a>.<\/p>\n

This separation of concerns already helps solving the confusion above. Under spec\/acceptance<\/code>, you should use only Capybara helpers. Inside spec\/requests<\/code>, you are using Rails provided tools. However, this does not solve the underlying problem that both helpers are still included in spec\/requests<\/code>.<\/p>\n

Therefore, while this blog post means to provide some guidance for those that run into such problems, we also would like to propose a solution that we discussed with David. The solution goes like this:<\/p>\n

1) We change RSpec to no longer generate spec\/requests<\/code>, but both spec\/api<\/code> and spec\/features<\/code> (I have proposed spec\/acceptance<\/code> but David pointed out those are not strictly speaking acceptance tests). The Capybara DSL (visit<\/code>, page<\/code> and friends) should not be included in spec\/api<\/code> under any circumstance.<\/p>\n

2) We change Capybara to include by default its DSL and RSpec matchers under spec\/features<\/code> and change the feature<\/code> method to rely on the type :features<\/code> instead of :requests<\/code><\/a>.<\/p>\n

The proposal suggests the addition of two new directories instead of changing the behavior of existing ones in order to be backwards compatible while ensuring a safe and more semantic future for everyone else. David asked me to outline our conversation in a blog post, so we can get some awareness and feedback before undergoing such changes. So, what do you<\/strong> think?<\/p>\n","protected":false},"excerpt":{"rendered":"

When David Chelimsky was visiting S\u00e3o Paulo in last April, we invited him to go out for some coffee, beers and brazilian appetizers. We had a great time and we talked about different topics like OO, programming languages, authoring books and, as expected, about testing. One of the topics in our testing discussion was the … \u00bb<\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[94,7,183],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2804"}],"collection":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=2804"}],"version-history":[{"count":8,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2804\/revisions"}],"predecessor-version":[{"id":2833,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/2804\/revisions\/2833"}],"wp:attachment":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=2804"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=2804"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=2804"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}