Blog


Nov

Why wait_until was removed from Capybara


The release of Capybara 2.0.0 removed the wait_until method from the API. This seems to have frustrated a few people, so let me explain why this decision was reached and what your options are for the future.

wait_until was removed for several reasons:

  1. It simply isn't necessary for most applications and test suites.
  2. Its existence confuses people into thinking that it is necessary, when in fact it isn't.
  3. It was added at a point where Capybara's auto-waiting was much more primitive than it is now.
  4. It used to be used internally, it no longer is.

Capybara has a had a very important feature since pretty much the beginning which is that it automatically waits for elements to appear or disappear on the page. If you do something like find("#foo"), this will block until an element with id "foo" appears on the page, as will has_content?("bar"), click_link("baz") and most other things you can do with Capybara.

Let's look at a more complicated case:

find("#foo").click_link("baz")

Even if #foo is originally on the page and then removed and replaced with a #foo which contains baz after a short wait, Capybara will still figure this out. Let's make that really clear, Capybara is ridiculously good at waiting for content.

For the most part, this behaviour is completely transparent, and you don't even really have to think about it, because Capybara just does it for you. What trips a lot of people up is that they try to do something like this:

page.find("foo").text.should contain("login failed")

And now they have introduced potential timing issues. text, being just a regular method, which returns a regular string, isn't going to sit around and wait for anything. It will simply return the text as it appears when Capybara gets to this line and call it a day. Now after a long debugging session, our developer has found the timing issue. They now realize that there is a wait_until method in the API, and immediately think that, "hey, this sounds like what I need!"

wait_until do
  page.find("#foo").text.should contain("login failed")
end

Fantastic, it now works! The thing is though, Capybara could have easily figured out how to wait for this content, without you muddying up your specs with tons of explicit calls to wait_until. Our developer could simply have done this:

page.find("#foo").should have_content("login failed")

Or even:

page.should have_selector("#foo", :text => "login failed")

And the problem would have solved itself.

As long as you stick to the Capybara API, and have a basic grasp of how its waiting behaviour works, you should never have to use wait_until explicitly.

Synchronize

Capybara 2 introduces a new method called synchronize. While this method is part of the public API, don't run off and use it just yet. It has a very distinct use case, which is completely different from what you're probably using wait_until for. You will most likely only ever have to call this if you access the low level driver directly through #native. In this case, you might receive errors like Selenium's StaleElementReferenceError. It's these kinds of errors that synchronize prevents.

Assserting on model objects

I am firmly convinced that asserting on the state of the interface is in every way superior to asserting on the state of your model objects in a full-stack test. In some cases, especially if your interface is asynchronous, you might still want to do it though.

This is the only legitimate use case for wait_until I've heard of in Capybara.

Imagine that in the following scenario, "Liked" is shown immediately, through JS, regardless of if the change was actually persisted to the server or not:

click_link("Like")
page.should have_content("Liked")
post.reload.should be_liked

This would cause timing issues, if the AJAX request is slower than the reload. We could have written this as:

click_link("Like")
page.should have_content("Liked")
wait_until { post.reload.liked? }

Where wait_until could be implemented like this: https://gist.github.com/d8da686061f0a59ffdf7

Though I personally would have preferred something like:

click_link("Like")
page.should have_content("Liked")
expect { post.reload.liked? }.to become_true

Which could be implemented like this: https://gist.github.com/4129937

You could have also asserted this through the UI:

click_link("Like")
page.should have_content("Liked")
visit current_path
page.should have_content("Liked")

But it is kind of verbose, and it's also a lot slower, so I can understand why the model test might be preferred.

But then why not just bundle it

If and when you need some kind of behaviour that waits for things, wait_until is a giant big sledgehammer. There are more fine grained, sophisticated tools built into Capybara, and I want you to learn about them, because those are some of the best features of the library. And when the built in tools aren't enough, there are more sophisticated tools available than that clunky hammer. So hopefully the removal of wait_until encourages you to write better tests.

Nov

Simple authorization in Ruby on Rails apps


Here at Elabs, we've been using CanCan for authorization in a number of applications. Ryan Bates managed to build an authorization system which is both simple and powerful. A step away from the bloated role based system available at the time, yet more sophisticated than simply tacking on methods on ActiveRecord models.

Over time though we've come against a few grievances with CanCan.

  • Ability files quickly become too big to manage, and there is no built in strategy for splitting up abilities across multiple files.
  • Even worse, there is no natural way to structure ability files. We usually resort to comments to divide the file into sections for different models.
  • All ability rules need to be evaluated for every request. While not a huge performance hit, it seems like a built in wastefulness.

And finally: at the time of writing, CanCan has 128 open issues, 28 open pull requests. Important functionality in the gem is broken, and attempts to fix it through pull requests are ignored. The test suite depends on ActiveRecord < 3.1 and won't even run with later versions of ActiveRecord, unless someone fixes this, we don't actually know if CanCan works at all with newer versions of AR.

In a recent project we worked on, we were running against bugs in CanCan which forced us to run a forked version, and we were fighting against an ability file which was growing out of control. We decided that we needed a new way to approach the problem.

Back to basics

We really like CanCan's simple approach. The ability file isolates all authorization logic, and it leaves you free to handle authorization however you want to. You are free to grow your authorization system from a single user role to whatever complexity you need. We were intent on keeping this flexibility.

We wanted something simpler though. Something which we can implement without really needing a library at all. We wanted to have full control over how the authorization system works.

We took inspiration from objectify and Bryan Helmkamp's excellent blog post 7 Patterns to Refactor Fat ActiveRecord Models among others and pared the whole thing down to creating a plain Ruby class for each domain model.

We call these classes policies and we put them in app/policies. They might look like this:

class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def create?
    user.admin? or not post.published?
  end
end

Using these classes from the controller is fairly easy:

def create
  @post = Post.new(params[:post])
  raise NotAuthorizedError unless PostPolicy.new(current_user, @post).create?
  if @post.save
    redirect_to @post
  else
    render :new
  end
end

This works quite nicely, but unfortunately it's a lot more code to write in the controller. Controllers are un-DRY enough as it is; we need to make this easier. It's simple enough to introduce a helper method for fetching a policy for a given record:

def policy(record)
  "#{record.class}Policy".constantize.new(current_user, record)
end

Now we can simplify our create method somewhat.

def create
  @post = Post.new(params[:post])
  raise NotAuthorizedError unless policy(@post).create?
  …
end

We can easily wrap this pattern in another method:

def authorize(record)
  raise NotAuthorizedError unless policy(record).public_send(params[:action] + "?")
end

And we end up with this:

def create
  @post = Post.new(params[:post])
  authorize(@post)
  …
end

That looks a lot closer to what we have in CanCan.

This pattern works fine for 6 of the 7 restful actions, but what about #index?

CanCan can automatically construct a query based on the permissions you have specified, as long as the hash based syntax is used, anyway. Unfortunately we've found this magic to be error prone and sometimes insufficient. We really want to use scopes for this, but we don't want those to pollute our model objects. Again, taking inspiration from Bryan's blog post, we create a class for this:

class PostPolicy < Struct.new(:user, :post)
  class Scope < Struct.new(:user, :scope)
    def resolve
      if user.admin?
        scope
      else
        scope.where(:published => true)
      end
    end
  end

  …
end

Usage for this from the index action is also fairly easy:

def index
  @posts = PostPolicy::Scope.new(current_user, Post.scoped).resolve
end

Again, we can simplify this with a helper method:

def index
  @posts = policy_scope(Post.scoped)
end

Both the policy and policy_scope method are especially useful in views. We can do things like this:

<% policy_scope(@category.posts).each do |post| %>
  <li>
    <h2><%= post.title %></h2>
    <p><%= link_to "Edit", [:edit, post] if policy(post).edit? %></p>
  </li>
<% end %>

Our views are kept quite nice and DRY, just like with CanCan.

We have bundled up these helpers in a very simple gem we're calling Pundit. It has a few more tricks up its sleeves, but basically it does exactly what this post outlines. We found that we could replace CanCan with this pattern very effectively. The resulting code is simpler, easier to understand and easier to test.

Conclusion

While we do think that Pundit has been useful to us, there is a bigger takeaway from this. We had a problem, and we threw a large library with a complicated DSL at the problem, and as the old saying goes, we now had two problems. Sometimes the simpler solution is better. Sometimes it makes sense to leverage Ruby, over creating your own mini language.

Spend some time reconsidering the dependencies you have in your application and whether they are actually helping you, or if you're spending more time fighting them than you're getting out.

Nov

Simple tricks to clean up your Capybara tests


I'd like to share with you a few simple tricks we use all the time at Elabs to clean up our Capybara tests.

Often we want to find a specific area of the page, and perform some action in that area. With the imminent release of Capybara 2.0, you will probably find yourselves doing this even more, since you will sometimes have to be more specific about which element you want to interact with, since Capybara will raise an error when more than one element can be found.

You are probably familiar with the within method in Capybara, so say you want to click a link in the main menu, you might do it like this:

within "header nav" do
  click_link "Archive"
end

But in Capybara, all methods like click_link and fill_in and so on can be chained, so you could have written the above more succinctly as:

find("header nav").click_link("Archive")

That's better. However, the central idea behind Capybara is that you should test your application the way a regular user would use it; your user knows what the main menu is, but they don't really know anything about that CSS selector.

I once wrote a blog post called "You're cuking it wrong", about best practices when using Cucumber, it ended with the following general rule:

A step description should never contain regexen, CSS or XPath selectors, any kind of code or data structure. It should be easily understood just by reading the description.

While it doesn't make sense to be as strict about this ideal in plain Capybara tests, there is still a point to be made about avoiding low level detail even in these tests.

With that in mind, let's revisit that example. What if we could simply write:

main_menu.click_link("Archive")

That's much more obvious. We clearly communicate the intent, and we don't include any irrelevant details in the test. The implementation is super simple:

module MenuSteps
  def main_menu
    find("header nav")
  end
end

Now you might ask, what if I want to perform a lot of actions in a particular area of the page, this doesn't look so nice:

login_form.fill_in "Email", :with => "jonas@elabs.se"
login_form.fill_in "Password", :with => "capybara"
login_form.click_button "Login"

Thankfully, the within method can actually take as an argument an actual element, not just a selector. So we can write this instead:

within login_form do
  fill_in "Email", :with => "jonas@elabs.se"
  fill_in "Password", :with => "capybara"
  click_button "Login"
end

Concise and clear.

Arguments

Of course, since main_menu and login_form are just methods, we could create similar methods which take arguments. Imagine we wanted to delete a particular comment on our blog. We could write this:

find(".comment", :text => "Worst article ever!").click_on("Remove")

Hopefully you can see where this is going:

comment("Worst article ever").click_on("Remove")

And the implementation:

module CommentSteps
  def comment(text)
    find(".comment", :text => text)
  end
end

Much better!

Assertions

This is a bit specific to RSpec, but if you're using a different test framework, you can adapt this to your needs.

In another test for our blog, we are creating comments, and we want to assert that the comment appears as intended.

page.should have_selector(".comment", :text => "Best article ever!")

Remember our guideline/rule. That selector has to go! It would be fantastic if we could simply write:

page.should have_comment("Best article ever!")

But now we have a problem. We could create an RSpec matcher, but that would actually be a bit cumbersome. It could look like this:

module CommentSteps
  extend RSpec::Matchers::DSL

  matcher :have_comment do |text|
    match_for_should { |node| node.has_selector?(".comment", :text => text) }
    match_for_should_not { |node| node.has_no_selector?(".comment", :text => text) }
  end
end

It's important that you add both match_for_should and match_for_should_not, otherwise Capybara's waiting behaviour doesn't work properly.

This also doesn't give us particularly great error messages. We will get some variation on "expected true to be false", which isn't particular helpful.

There is a much easier way, which works well for simple cases like these:

module CommentSteps
  def have_comment(text)
    have_selector(".comment", :text => text)
  end
end

Same functionality, but easier to understand, shorter, and it even gives us better errors. We are making use of the fact that have_selector just returns an RSpec matcher.

Ensure on

One thing we face often in our tests is that we need to be on a particular page to do something. This is especially important if we have abstracted something into a helper. Consider this:

module SessionSteps
  def login
    visit login_path
    fill_in "Email", :with => "jonas@elabs.se"
    fill_in "Password", :with => "capybara"
    click_button "Login"
  end
end

But what if we already are on the login page when we call login, we'll visit the page again, which wastes valuable execution time. Let's make sure we don't do that:

module SessionSteps
  def login
    visit login_path unless current_path == login_path
    fill_in "Email", :with => "jonas@elabs.se"
    fill_in "Password", :with => "capybara"
    click_button "Login"
  end
end

We immediately spot that this pattern would be sensible to abstract. We have this in almost all of our projects, and we call it ensure_on:

module CommonSteps
  def ensure_on(path)
    visit(path) unless current_path == path
  end
end

Usage should be fairly obvious:

module SessionSteps
  def login
    ensure_on login_path
    fill_in "Email", :with => "jonas@elabs.se"
    fill_in "Password", :with => "capybara"
    click_button "Login"
  end
end

And for our final trick…

Suppose that on our blog, comments are moderated, and when someone posts a comment, we want to check that it appears in the moderation queue:

fill_in("Comment", :with => "Hi there")
click_on("Post")
visit moderation_queue_path
find(".moderation-queue").should have_comment("Hi there")

Of course we should create a moderation_queue method, like we did in the first example. But then we still need to remember that we need to actually visit that page before we fetch the element. Let's abstract that away:

module ModerationQueueSteps
  def moderation_queue
    ensure_on moderation_queue_path
    find(".moderation-queue")
  end
end

Now we can write:

fill_in("Comment", :with => "Hi there")
click_on("Post")
moderation_queue.should have_comment("Hi there")

That reveals our intent much clearer. We have practically created a small little DSL for our tests, just by creating a couple of really simple helper methods.

So that's it, some really simple tricks which allow you to write cleaner, more understandable tests with Capybara.

Oct

Give a shit about copy


I follow the blog “Great not Big” by Carl Erickson at Atomic Object. Yesterday he wrote about their value mantra “Give a shit”. I think it’s a brilliant way to express that you care about what you do. He ends the blogpost with the question if others would rather use “care deeply” or “give a shit” in public and why.

I definitely say “Give a shit”. I work with copy and it’s hard. Many companies use the same phrases, and most of them sound hollow. When potential clients read the same things and promises over and over again it has no impact. I think the phrase “care deeply” will go unnoticed or leave the reader with the feeling of empty promises.

If you instead read “Give a shit” you will react. Some may be offended but I think most people will react positively and, most importantly, remember you. “Give a shit” will stick. It will leave the client with the feeling that you mean what you say, and that is good copy.

Copy shouldn’t be written to sound good or suck up to clients. It should reflect how we work, be straight forward and honest. No bullshit. At Elabs we work with an open and constant dialog with our clients, we discuss and advise. Our copy should reflect that, so our clients know what they can expect. Right now it doesn't, and we're working on changing that.

A phrase that I think unfortunately is used too often in our business, and therefore lost its appeal, is “handcrafted”. The initial thought about using the term handcrafted about development was good, even great. It emphasizes that developers care about their work, care about details and about building something with quality to be proud of. But when too many companies use the phrase it loses its meaning. It won’t stick. At Elabs we've used the word bespoke instead. The problem is that it’s actually the same word, just sounds fancier. And it’s not memorable.

So how do we convey the meaning of handcrafted without using the word? I don’t know. But if we want to stand out and make clients react and remember us we must give a shit about our copy.

Sep

A conference should be an experience filled with joy


Jonas Downey at 37signals wrote about their experiences at the XOXO conference in Portland last week. It sounds like it was a great conference, and the principles behind it echoes our ideas for Nordic Ruby. This passage from the blog post about XOXO sums up Nordic Ruby as well:

“Conference events shouldn’t be short and formal. Andy Baio and Andy McMillan could teach s course in throwing a good event. In short: allow your attendees plenty of room to breathe. Don’t pack in tons of simultaneous sessions in a generic hotel. Give people an experience. Give them free time. Give them good food and loads of coffee in a weird place.”
— Jonas Downey, Lessons in creativity and joy at XOXO

We share the XOXO organisers belief in having fun together. To create social time for the attendees. The conversations and new connections you make at a conference are important. So putting everyone in one place for a couple of days with a spacious schedule and parties in the evenings open up for socialising which, like Jonas writes in his blog post, is invaluable.

To read more about our thoughts on Nordic Ruby, check out CJ's blog post about Nordic Ruby 2012.