Designing Code with RSpec

Written by: Clemens Helm

10 min read

Stay connected

https://fast.wistia.com/embed/medias/whr5foo4xe.jsonphttps://fast.wistia.com/assets/external/E-v1.js

This is the tenth Testing Tuesday episode. Every week we will share our insights and opinions on the software testing space. Drop by every Tuesday to learn more! Last week we talked about using stubbing and mocking to specify your examples behavior-driven with RSpec.


Design your code with stubs in RSpec

It's great to be able to stub existing code to become independent from other components' behavior. But it's even better that you can stub code that doesn't exist before you implement it.

Stubbing code that isn't even there yet is a great way of designing your application. Instead of taking the code that already exists, you think of the code you wish you had. Stubs let you specify what you wish your code looked like by simulating methods the way they should behave. Step by step you implement the stubbed methods afterwards, guided by your integration tests that point out what to implement next.

This approach leads to readable, maintainable and reusable code. In this screencast we walk you step by step through this design process.

Up next Testing Tuesday: Reliable mocks with Bogus

In the last two Testing Tuesday episode we made sure that all our stubbed and mocked behavior also works in the "real" world by running our Cucumber scenarios. This can become cumbersome though. A faster approach is to use Bogus, that checks the methods you're stubbing for you. Learn more next Testing Tuesday!

Further info:

Transcript

Ahoy and welcome! My name is chief mate Clemens Helm and you're watching Codeship Testing Tuesday #10.

Last week we specified single components of our application using stubbing and mocking. We stubbed existing behavior of other components to become independent of their implementation.

This week I show you how to stub code that isn't even there yet. This will improve the way you design your code.

== Intro End

We'll keep working with last weeks application. We've got a user with a first name, a last name and a full name. And a user has many projects.

And we've got projects that have a name.

Right now, every visitor of our application can access all projects, but we'd like only team members to access their projects.

As usual we write a Cucumber feature first:

Feature: Restricting project access
  In order to keep a project private
  As the project's owner
  I only want team members to access the project
  Scenario: Accessing a project that I'm not team member of
    Given I'm signed up as "NSA"
    When I try to access another user's project
    Then I should not see the project

And these are our step definitions: We reused the first step from last weeks example. The other two steps are

When(/I try to access another user's project/) do
  another_user = User.new name: "Another user"
  @project = Project.new
  @project.user = another_user
  @project.save!
  visit project_path(@project)
end
Then(/I should not see the project/) do
  page.current_path.should_not == project_path(@project)
end

When we run this feature, it fails, because so far we haven't got any restrictions what should be accessible or not.

Let's write an example for our projects controller to prevent access:

# projects_controller_spec.rb
describe ProjectsController do
  it "should check if a user is team members" do
    user = double
    project = mock_model Project
    ProjectController.any_instance.stub current_user: user
    Project.stub find: project
    user.should_receive(:member_of?).with(project)
    get :show, project_id: project.id
  end
end

Here we use mock_model to simulate a project because it also creates an id for the project that we can pass to the controller action. We also simulate that there is a logged in user by stubbing the current_user method on any instance of the ProjectsController. We use this approach because the instance of the ProjectsController will be automatically created for us.

But what's really nice is that we can use our example to specify the code we wish we had. So far our User model doesn't have a method member_of?. But it would read nicely, so I decided that a user should have this method. Let's make this example work:

class ProjectsController < ApplicationController
  def show
    @project = Project.find params[:id]
    current_user.member_of? @project
  end
end

Great, now it works! But in fact this doesn't change the action's behavior. What we want is to redirect users to another page if they aren't team members of the project. So let's add an example for this:

describe ProjectsController do
  it "should redirect non-team members to their projects overview" do
    user = double member_of?: false
    ProjectController.any_instance.stub current_user: user
    response.should_redirect_to projects_path
    get :show, project_id: project.id
  end
end

To make this example work we could also write the following:

class ProjectsController < ApplicationController
  def show
    @project = Project.find params[:id]
    current_user.member_of? @project
    redirect_to projects_path
  end
end

But this would redirect all users to the projects path. So one more example:

But before we do that let's clean up our examples first. Like application code you should also refactor your examples to avoid repetition and keep them clean. Consider refactoring your examples everytime you made them pass, but refrain from refactoring when there are still failing examples, this will make everything much worse. Trust me, I've been there.

Let's move all our duplication into a before each block. This will be executed before each example.

describe ProjectsController do
  it "should show the project to team members" do
    user = double member_of?: true
    ProjectController.any_instance.stub current_user: user
    response.should_render_template :show
    get :show, project_id: project.id
  end
end

When we change our application code now

class ProjectsController < ApplicationController
  def show
    @project = Project.find params[:id]
    redirect_to projects_path unless current_user.member_of? @project
  end
end

all examples work. Great!

So we've got a perfectly readable controller action by specifying code that doesn't exist yet. I like this method of designing a component's interface, because usually it's much easier to specify simple code than complicated code. So when you stick to this pattern, your code will become more readable and more structured automatically.

But where do we go from here? Well, we've specified everything we need from our controller, so let's run our scenario again.

It fails telling us that there is no method member_of? for User. So our scenarios are the always safe-guards that remind us to implement everything that's still missing to make our new feature work.

So let's implement this method now. A user should be a member of a project when it has got a project membership. So we define one example for our User model:

describe User do
  context "verifying a membership" do
    it "should query a membership" do
      memberships = double
      user = User.new
      project = mock_model Project
      user.stub memberships: memberships
      memberships.should_receive(:where).with project_id: project.id
      user.member_of? project
    end
  end
end

So we want to check for a membership with the project's id in the database. Let's make this example work:

class User < ActiveRecord::Base
  def member_of? project
    memberships.where(project_id: project.id)
  end
end

But now this method will return a database relation instead of true or false. So we need a second example:

describe User do
  context "verifying a membership" do
    it "should be a member if a membership exists" do
      user = User.new
      project = mock_model Project
      user.stub_chain(:memberships, :where, :exists?).and_return true
      user.member_of?(project).should be_true
    end
  end
end

The easiest way to make this work is to return true in our method:

class User < ActiveRecord::Base
  def member_of? project
    memberships.where(project_id: project.id)
    true
  end
end

But that's not what we want. Let's add one more example:

describe User do
  context "verifying a membership" do
    it "shouldn't be a member if no membership exists" do
      user = User.new
      project = mock_model Project
      user.stub_chain(:memberships, :where, :exists?).and_return false
      user.member_of?(project).should be_false
    end
  end
end

Now our implementation will look like this:

class User < ActiveRecord::Base
  def member_of? project
    memberships.where(project_id: project.id).exists?
  end
end

Much better!

Let's run Cucumber again. It tells us that there's no method memberships for our User. So the only thing left to do now is to create the membership relation between the user and the project. With Rails we can create this relationship on the command line:

rails generate model Membership user:belongs_to project:belongs_to

Now we only need to migrate the database and also propagate the changes to the test database:

rake db:migrate && rake db:test:prepare

And we tell the user that it has got membeships now:

class User
  has_many :memberships
end

If we run our feature now, it succeeds.

The nice thing about this outside-in approach is, that you define the methods you need first and take care of their implementation later. Without this approach we would have probably started by creating the Membership model and then we would have written a controller action. But then, the controller action would have probably looked like this:

def show
  @project = Project.find params[:id]
  unless @project.memberships.where(user_id: current_user.id).exists?
    redirect_to projects_path
  end
end

This action is much less readable than the one before. We deal with an abstract Membership term instead of just asking if a user is member of a project. And we increased complexity by having a long method chain with a database query in our controller action.

But what's even worse: This code is not reusable, whereas our member_of? method can be used many times throughout the application. Of course you could also refactor this controller action and create the member_of? method afterwards. But this would be an additional step and these get forgotten quite often. With our outside-in approach this code just evolved naturally out of our process.

However, some of you told me that they don't like running the integration tests to verify that their stubbed behavior still works, because it slows them down.

I tend to run only unit specs and the Cucumber feature I’m currently working on on my local machine. Once I’ve completed a feature I push it to GitHub and the Codeship will pick it up and run the entire feature suite for me. If something broke, it would inform me. So I can keep working while the Codeship checks my current code.

I’m a big fan of having integration tests check the entire system's behavior before I ship something. However, if you just want to verify that your mocked behavior actually works while you're developing a feature, this feedback cycle can be cumbersome. Especially when you’ve got an extensive integration test suite, getting feedback from your continuous integration system can take 20 minutes or more.

Outro

Fortunately there are tools that verify your mocked behavior faster. But that's enough for today. Thanks to Rafael and Cezar who suggested "bogus" to solve this problem. We'll take a look at it next week.

See you next Testing Tuesday, and just remember one thing: Always, really always stay shipping!

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.

Loading form...
Your ad blocker may be blocking functionality on this page. Please disable for an improved experience.