Select Mode

Thinking and Testing with Executable Specs – Part 1

I’ve been doing some presentations and training sessions on using tools like Cucumber. What’s interesting about these is that in many cases people have tried to play around with the tool but have conceptual difficulties with figuring out how to get started in terms of making decisions continuing to using the tool.

As a tester, when introducing yourself to this area of practice, you might read that You’re Cuking It Wrong or that You’re Cuking It Right. It still may not be clear which you are doing. You might read about being imperative versus declarative and wonder which is for you. (Can’t I do both?) You might even come to the conclusion that Cucumber sucks.

So let’s jump into an example here. In fact, this is going to be a very example-driven post. I’ll assume you have Ruby installed. (I’d recommend 1.9.2 or later.) You’ll also need the Cucumber gem installed. Since I’m going to be using Cucumber outside of Rails and to test web applications, I need to pick a browser driver. I’ll be using Capybara although really my focus is on Cucumber so you could use something like Watir Selenium or whatever you want. If you want to following along make sure you have the capybara gem installed.

In order to give us something common to work on, I’ve focused on a very simple little application I wrote in Ruby using Sinatra. Calling it an application is even a bit of a misnomer. You can download this file from GitHub. Either clone the repo or just download it. You can just follow along without even worrying about the sample application. It’s up to you. If you do want to follow along, you’ll need the datamapper and sinatra gems installed.

First create a project directory where you can work. If you want to follow along directly, you’re going to put the sample app in this project directory. It doesn’t matter what directory name you give it: just download the sample app and unzip it into your project directory.

Assuming you called your project directory Projects and you made a directory called app for the sample application, here’s what this looks like now:

Projects
+----app
    |   app.rb
    |
    |----config
    |       database.rb
    |
    |----models
    |       plan.rb
    |       product.rb
    |       study.rb
    |       user.rb
    |
    |----public
    |   +---css
    |           style.css
    |
    +----views
            create_plan.erb
            create_product.erb
            create_study.erb
            create_user.erb
            landing.erb
            layout.erb
            login.erb
            login_page.erb
            plans.erb
            products.erb
            studies.erb
            test_page.erb
            users.erb

Now, within your project directory, create a file called cucumber.yml. The only thing that needs to go in this file is this:

Here I’m just setting up a default profile that will serve for testing purposes. The profile will not run any scenarios that are tagged “wip” (meaning, of course, ‘work in progress’). The main thing, however, is that I’m going to use a different structure than Cucumber’s and so I use the require switch (-r) to do a few things. Here’s a few things to understand:

  • I don’t use the support directory that Cucumber expects.
  • I don’t use the env.rb file (in the support directory) that Cucumber expects.
  • I don’t use the features directory that Cucumber expects.

So what do I use? Well, let’s create the directory structure now. Create a directory structure like this:

Projects
+----specs
    +----engine
    +----steps
    +----tests

Within your project directory, you’ll have a specs directory. As you can see, your cucumber.yml file lives outside of the specs directory. That’s because when you run the cucumber command, you will do it from the project directory. It will find the cucumber.yml file (which it does by default) and then that file will use the require (-r) parameters to tell Cucumber where to look for what it needs to execute.

Here’s how a typical Cucumber setup would look, just for comparison purposes:

project
+----features
    +----support
    +----step_definitions

So let’s just make sure we’re all on the same page here. This is what your directory structure should look like right about now:

Projects
|   cucumber.yml
|
|----app
+----specs
    +----engine
    +----steps
    +----tests

The app directory is where my sample application resides although, again, you can name that anything you want. You might be thinking that I have this sample application just for purposes of this post. Well, partially true but, in fact, I often have little sample applications like the above that I can run for test purposes, even if I’m not connected to the internet. Having a local application like this is handy. The fact that I wrote it in Sinatra means I can have it be an application that works in a browser as well.

Okay, back to work.

In the engine directory, create a file called borg.rb. Note that this is the file that I required as part of the cucumber.yml file. Since I don’t use env.rb, I have to tell Cucumber what file it should load up first. Also, now you can see why I specify a specs directory in the default profile. This specs directory stands in for Cucumber’s features directory. I like to do this because it shows right away that I can use Cucumber my way rather than necessarily having to worry about all of Cucumber’s conventions. (After all, the tool should work for me rather than the reverse, right?)

Borg? Really? Yeah. This stands for Business Object Runtime Generator. It’s part of my normal framework stuff. I do stuff like this a lot. I choose to believe this reflects a charming personality quirk rather than an annoying character flaw.

The steps directory will contain what I like to call test steps but Cucumber refers to as step definitions. The tests directory is where I will put feature files. (Ideally I’d like to call them something other than feature files since oftentimes what I’m dealing with is a bit more granular than a feature; really more of a capability within the context of a wider feature.)

The borg.rb file is acting like the env.rb file that Cucumber expects. That file is used to set up the “environment” that will be used for testing. So here is where you’d set up your browser driver. Since I’m using Capybara, here’s what I have:

If you wanted to use Selenium or Watir, you’d do whatever setup or configuration was needed for those libraries here. The purpose of this post is not to explain the browser drivers so I’ll leave the above somewhat unexplained for now. Just realize that I’m telling Capybara to use Selenium as the driver for my tests and I’m telling it where it’s going to go to run those tests. Here you’ll notice that Capybara is being told to read a local URL. That’s the default location that my Sinatra application will run at.

At this point, you might want to run the cucumber command just to make sure the basics work. Nothing too exciting will happen in terms of test execution. In fact, the output will probably look something like this:

$ cucumber
Using the default profile...
0 scenarios
0 steps
0m0.000s

But at least we know things are working correctly. Had the cucumber.yml file had some problems with it, Cucumber probably would have reported some errors.

So now we can focus on the important stuff. How do you actually approach writing Cucumber scenarios and getting into a rhythm with it? Well, first let’s consider the helper application.

I modeled this bare bones implementation on a virology helper lets scientists conduct trials of viruses that are found in the wild to determine their lethality and their ability to wipe out all human life. (It’s a fun job, filled with good cheer.) Clinical scientists will conduct their trials under a plan. This plan has to be done with a specific “product” — meaning the viral agent under test. The plan itself must be attached to a particular study. Further, different user roles can do different things in the system. What I wanted here was a system that had some elements that depended on other elements so that I could craft test steps accordingly.

To run the Sinatra application, you’ll want to run this command in the directory where you put the sample application:

ruby app.rb

My sample application is about as bare bones as can be. It’s designed just enough in order to to showcase what I want to talk about here. I wrote this using Sinatra and DataMapper, using an SQLite3 database. This gave me a good chance to learn those technologies but also provide something nice and simple that could be used just about anywhere. Testers write tools! Even simple ones.

As part of testing this application, my initial focus was predicated upon having no “gold” or standard test data that I could always rely on. Instead, the emphasis was on creating test data as and when test conditions warrant the use of that particular data.

Now, the rest of this is me taking you through my process. Think of this as an experiential case study that you live through along with me. I haven’t sanitized this, so you’ll find some false starts and probably what amount to some bad practices. I’ll probably jump between first and second person.

Not knowing quite where to start, I figured I’d go for the user creation bit since that was going to be central to everything I did. Any action taken would require a user of some sort. So create a file called create_user.feature and store that file in specs/tests. Then put the following in that file:

Feature: Ability to create users

Scenario: Create a user
  When I create a "Clinical Administrator" user

If you’re familiar with Cucumber, you’ll probably immediately notice that I don’t have a Given or a Then clause in this. Remember, I’m just starting out with what I immediately need rather than worrying about exactly how a feature file or scenario is “supposed” to be structured. I’m not even sure how to get that statement alone to work yet. I just typed what I wanted without worrying about implementation details. My logic here was that I knew I needed certain user roles to be in the system. I knew some of those users would have to be created and used as part of my test fixtures.

Now create a app_steps.rb file in your specs/steps directory. I’m sure I’ll have more step files at some point so right now I just created a generic one. Then create this placeholder for the step:

If you run the cucumber command again, you’ll see that it works insofar as a you get a “TODO (Cucumber::Pending)” line saying that a test clause could be matched to a test step, but there’s no logic. I have to put something in place of pending. But what? Well, I realized that to execute this step, I required a person being logged in to the system. So I tagged that scenario as @wip and tagged it as @starting. In other words, the start of my scenario looks like this:

@wip @starting
Scenario: Create a user
  When I create a "Clinical Administrator" user

Tagging scenarios like this in Cucumber is a really powerful feature. You can essentially set up up “execution profiles” based on the tags you set up.

The @starting is just a note to myself to tell me this is what I started off with. The @wip, as mentioned earlier, refers to “work in progress.” My default profile in cucumber.yml makes sure that any scenarios tagged as @wip will not be executed.

Then I started another scenario in that same file:

Scenario: Login as a role
  Given I am logged in as an administrator

Although it doesn’t matter from an execution standpoint, I put this scenario above my starting one. I do that because this new scenario is one that would have to be executed first and so, conceptually, as I read my file that’s how I like to see things.

The next step is easy, so I put this in my app_steps.rb file:

As with my feature file, I put this above my previous code and, again, that’s not because I’m required to do so but, rather, because as I’m going through this exercise I like to consider whether my steps are more of an action step or a setup step as well as whether they are more specific or more general.

Except that now I realized I needed the user to be on the login page in the first place. So I stopped that scenario — made it a @wip — and made yet another one:

Scenario: Go to login page
  Given I am on the login page

The step for this is simple:

So just to ground you to a reference here, this is what my create_user.feature file looks like at this point:

Feature: Ability to create users

Scenario: Go to login page
  Given I am on the login page

@wip
Scenario: Login as a role
  Given I am logged in as an administrator

@wip @starting
Scenario: Create a user
  When I create a "Clinical Administrator" user

My most specific step, and my starting point, is at the bottom. I had the backward evolution of that step going up from there. Likewise, my app_steps.rb file looks like this:

All of this was providing me with a structured way to consider my tests and, more importantly, how I wanted to word the specification part. Ultimately I wanted that initial specification — the @starting one — to work. But I realized I had to do some work to get it in place while also keeping in mind that I did want to keep the number of step phrases that someone had to use to a minimum.

So I think this was a good starting point. We got a project going and we started out with some thinking around how to write the test specifications which will, ultimately, become executable tests. The key point here really was the thinking; I was thinking about the highest-level step I wanted to deal with at the time. That then led me into thinking about the lower-level steps that would allow me to express my higher-level ideas. I believe this is a key aspect of working with tools like Cucumber. In the next post, we’ll start driving this application and refining the steps as we learn more.

Share

This article was written by Jeff Nyman

Anything I put here is an approximation of the truth. You're getting a particular view of myself ... and it's the view I'm choosing to present to you. If you've never met me before in person, please realize I'm not the same in person as I am in writing. That's because I can only put part of myself down into words. If you have met me before in person then I'd ask you to consider that the view you've formed that way and the view you come to by reading what I say here may, in fact, both be true. I'd advise that you not automatically discard either viewpoint when they conflict or accept either as truth when they agree.

3 thoughts on “Thinking and Testing with Executable Specs – Part 1”

  1. It seems like the example application is no longer available. Have you renamed it, or is it just gone? Any chance you still have it somewhere? I’d love to go through the examples. Feel free to email me. Thanks!

    1. Ah, this is a bummer. Yeah, the sample_app was lost during a huge GitHub snafu with my account. I will see if I can dig up a copy that I had as a backup. I will most likely have to redo these articles using my newer “sample app”, which is called Dialogic. It’s a Heroku app but can also be downloaded and run locally via the Dialogic repo.

      Sorry about the confusion and inconvenience on this. It’s a terrible oversight on my part.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.