SitePrism 2.4 – #find, #has_no_element? & Capybara 2.1

SitePrism 2.4 is out! It contains the following changes:

  • It uses #find instead of #first to locate items on a page (this should increase the reliability of your tests) – you don’t need to make any changes, this is an ‘under-the-hood’ fix :)
  • A new #has_no_<element>? method has been added
  • Capybara 2.1 is now a dependency

Migrating a Capybara test suite requires a few changes to your code; since SitePrism sits on top of Capybara you’re going to have to make a few tweeks. Fear not, I’ve summarized them here:

In your Gemfile (assuming you use Selenium as your browser driver), add the following line:

  • gem ‘selenium-webdriver’

In your Capybara config block (probably in your env.rb file if you’re using cucumber or your spec_helper.rb file if you’re using rspec), add the following lines:

  • config.match = :prefer_exact
  • config.ignore_hidden_elements = false

The above will replicate the behaviour of Capybara 1.*. If you want more details, take a look at Jonas Nicklas’ post entitled Introducing Capybara 2.1, particularly if you want to replicate Capybara 2.0 behaviour rather than 1.* behaviour.

Enjoy!

SitePrism 2.0

SitePrism 2.0 is out! Woo! So what’s changed in the API?

Nothing. Nothing at all.

So why release a new version? Well, all previous versions of SitePrism were dependent on Capybara 1.x. Now that Capybara has moved on to 2.x, SitePrism needed a few changes under the hood to get it to work with the new Capybara. That’s all! It just makes sense for SitePrism to be on the same ‘major version’ as Capybara.

Oh, there is one thing: since Capybara 2 doesn’t work with ruby 1.8 any more, SitePrism doesn’t either. You’ll need ruby 1.9 to use the new SitePrism.

Managing your page objects

The page object model works very well, but there are a few traps you can fall into. Alister Scott goes through a few of them and mentions one that particularly grates me: “Pages stored as instance variables”. Here’s a demonstration of the problem:

So why is that a problem? Well, there’s lots of noise – 3 lines are there just to create variables. This significantly reduces readability as you’ll end up with many, many lines of test code just creating instances of page objects. You also now have a whole load of instance variables to keep track of: @login_page, @account_page, @account_history_page. And when you’re using cucumber to run your tests, this will lead to *big* maintenance issues. I’ve had to rescue a few cucumber-based test projects and one of the most frequent causes for test-rot is that the testers lost track of their instance variables. Been in this situation before?

“Can I use @account_page here? Did I previously declare it? Hmmm… No, it’s nil when I try to use it. OK, I’ll instantiate it here. [runs the tests]. Cool, that works. Oh no! Doing that has broken some other tests that referenced @account_page but expected something else!?!? Should I fix up the other tests? Rename the @account_page variable to something else? If I do that will I break anything else?” Not fun. Big spaghetti problems.

But what to do about it? Alister suggests using blocks (provided by the page-object gem) that look something like the following:

On first glance, this looks great. No instance variables to keep track of. Just deal with the classes themselves and use only local variables inside the blocks. Nothing to keep track of. Great!

But…

The above proposal causes other maintenance hassles – the page object’s class name is now scattered throughout the code. Lots of “visit LoginPage” all over the place. What happens when the class name changes? You’ll have to make changes throughout the code. Not fun.

Solution: use an instance of an App class, this App class being a representation of the app you’re testing (the whole app, not individual pages). This App class contains one method per page class, each of these methods return an instance of the relevant page class.

“Whhhaaattt?”

OK, here’s an example:

In your tests, you would then have the following line:

When I visit the login page

…which would match the following step…

If you structure your tests such that they always begin by mentioning where the user starts (a good idea as it gives context), you can rely on the fact that @app has been instantiated so you can just use it. For example:

So how is this an improvement? Well, there’s no need to manage instance variables for different pages – just call methods on a known instantiation of the App class(@app) and they’ll return instances of the pages you want. There’s no need to mention class names; they are hidden behind methods. If the class name changes you only need to make one change (change the class referenced in the methods in the App class).

Subjective statement: I’d also argue that you also get great readability with this way of structuring things.

This is how I’ve normally organised things. And it’s worked great both on small projects of only 10′s of tests to large projects where the number of tests is 1000+. It’s the best solution I’ve come up with, it doesn’t suffer from having instance variables all over the place, neither are there class names all over the place.

I’ve written up in brief how this works if you’re using SitePrism to manage your page objects: http://rdoc.info/gems/site_prism/file/README.md#Epilogue

SitePrism: Capybara Page Objects

Capybara is a great browser interaction library for automated testing, but until now it hasn’t been much fun to follow the Page Object pattern with it. So, I wrote SitePrism that lets you do just that: SitePrism is a Page Object Model DSL for Capybara.

We’ve been using it at my current client since December 2011 and it’s proven a great success. Until now I’ve kept it quiet to allow some time to develop it to the point that it does everything we need, but a few days ago SitePrism went to version 1.0 – early adopters tell me that SitePrism’s intuitive API lets them create expressive and maintainable Page Objects with ease, so take a look! The SitePrism ReadMe will give you an idea of what it’s like to put together a Page Object model of the site you’re testing using SitePrism with capybara…

Here’s the code: https://github.com/natritmeyer/site_prism
And here’s the documentation: http://rdoc.info/gems/site_prism/frames

Go take a look, it might save you a lot of frustration!