We’ve mentioned our love of page objects in feature testing. Setting up the environment page objects, our pattern is to implement method missing to handle methods of type _page . When a step requests login_page , method missing picks up the request, lazy loads a LoginPage into a instance variable on demand. That @login_page instance is then available to any subsequent step in the spec through the login_page method.

module ProjectWorld def method_missing ( method , * args , & block ) if method =~ /_page$/ variable_name = "@ #{ method } " ivar = instance_variable_get ( variable_name ) unless ivar page = method . to_s . classify . constantize . new ivar = instance_variable_set ( variable_name , page ) end ivar else super end end end World ( ProjectWorld )

This is possible because any instance variable that is set in the World of a Cucumber feature spec is globally available to any other step of that spec. This applies not only to page objects, but to any data models that are created or factory’d up into instance variables. We found ourselves doing things like this a lot in our steps.

Given ( /^there is an active promotion with title "(.*?)"$/ ) do | title | @active_promotion = FactoryGirl . create ( :active_promotion , title: title ) end Given ( /^the active promotion has a message "(.*?)"$/ ) do | text | FactoryGirl . create ( :message , promotion: @active_promotion , text: text ) end

The assumption in the second step makes makes me uncomfortable, i.e. some previous step has set up @active_promotion . Keep in mind these two steps could have been defined in completely different files, and still work. Global accessibility of instance variables in Cucumber seems wrong, but it can be rationalized I suppose. You can be relatively assured that each spec is a self contained little program with a relatively small scope.

We can avoid this in our data objects though by catching calls to them and lazy loading, similar to what we do with our page objects. Since we’ve given in to the idea of global state in our specs, why not apply this same pattern to our short-lived data models? Chris Nelson and I decided to give it a try on our last project.

It turns out that factory_girl makes this super simple. factory_girl makes it easy to represent a model in it’s various states by just setting it up and referring to it by name. For example it’s easy and preferable in factory_girl to define representations for a promotion like this:

FactoryGirl . create ( :active_promotion ) FactoryGirl . create ( :pending_promotion )

rather than:

FactoryGirl . create ( :promotion , status: "active" ) FactoryGirl . create ( :promotion , status: "pending" )

This makes it easy define simple accessors in our Project world that return very specific, consistent data objects. Our module iterates through each defined factory, defining a lazy-loading accessor for each:

module ProjectWorld def method_missing ( method , * args , & block ) ... end FactoryGirl . factories . map ( & :name ). each do | factory | define_method ( factory ) do variable_name = "@ #{ factory } " ivar = instance_variable_get ( variable_name ) unless ivar object = FactoryGirl . create ( factory ) ivar = instance_variable_set ( variable_name , object ) end ivar end end end

We can now effectively change the above steps to this:

Given ( /^there is an active promotion with title "(.*?)"$/ ) do | title | end Given ( /^the active promotion has a message "(.*?)"$/ ) do | text | FactoryGirl . create ( :message , promotion: active_promotion , text: text end

The active_promotion method in the second step has been defined in our module. It will look for an instance_variable @active_promotion , and return it if it is defined. Otherwise it will set that @active_promoiton to a newly instantiated active_promotion factory. This effectively makes the first step, which was responsible for setting up the @active_promotion , a NOOP.

This pattern allows us to access models in the same way in every step, having confidence that it will never be nil.