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.

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!

Transcript

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

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:

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

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

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:

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

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

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:

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!

Subscribe via Email

Be sure to join 36,336 subscribers of our newsletter to receive updates on software development best practices, Continuous Delivery and tips and tricks to start shipping your product faster.

Email Address

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

Lars Schirrmeister

Hi! Nice episode and thanks!

But there is a thing which I don’t like so much about this way of testing.

If I see it right, the test is telling a lot about the implementation details. Each test which does a mock in that way allows only the call on that specific method and even worse the should _receive does not allow to change the code while keeping functionality. What if I want to use for example a certain join for finding projects where the user is a member of. I could be, that the functionality stays exactly as it is, but the implementation would change and the tests would fail. Maybe at the end we should to try to refactor the tests to use the stubs and should_receives as few as possible.

Clemens Helm

Hi Lars,

Thanks for your comment. Recently I’m also thinking about these issues a lot.
How would you write this episode’s examples?