By Pablo Ifran

In this post, I will describe different techniques I generally use for testing Ruby on Rails apps. I’m going to use as an example a simplified version of a blogging platform we have developed for a client. The app basically allows users to manage and publish stories, which are posts, but more complex – e.g. Stories are versioned. To simplify the examples I will leave complexities aside and talk about stories as if they were posts.

Note: I’m assuming that you already know the importance of automated testing, so I’m not arguing its importance here.

Diving into the details, let’s start with the Story model:

Within the Story model we have a scope called “drafts”, that returns all the stories from our database which are drafts (have the attribute draft set in true). By default stories are not drafts. Now that we have defined the scope, we need to think about all the possible scenarios that need to be tested.

If we take a look at the scope, it’s easy to realize that we have two possible situations:

The first one is when the story is a draft and the other one is when it’s not. Hence, we can divide our tests like this:

Now that we have a clearer idea of the possible scenarios, we can start adding in tests. In this case, since each scenario (context) will only have one test, we can take advantage of the “let!” method.

Note: using the let! method might not always be considered as a good practice, because it generates a before hook and then executes and memorizes the content of the given block. Thus, for each test we will have the block content available, and if it’s a database query it will be executed several times, even if we don’t need it.

In our case we can easily realize that we won’t have more tests per scenario, so we can use let! without worrying about the performance.

Let’s create the tests:

In this example our tests are based on data, meaning that they rely on database queries. If our method implementation changes, the tests will still pass, as long as the result is the expected one. These tests are recommended for model methods handling data, changing the model status or, like in this case, for database queries.

Now we can create stories and make them “draft” or “published”, but we are not done yet. Having a story without a cover photo will not look nice. So our next step will be adding the ability for a story to have one.

We will do some photo processing, to make sure that all the cover photos follow the same standard:

This is a more complex situation: we have a few scenarios when the cover photo is processed and some more when it is not. Furthermore, the cover photo could even not exist.

Let’s start with the contexts we need to define. A good approach could be defining one case for when the cover photo hasn’t been fully processed yet, and another case for when the background processing has completed. For the first case, we have two scenarios, depending on whether the cover photo exists or not.

Contexts definition:

Now that our contexts and test cases are well defined, we can start implementing them:

This is, once again, the same pattern: tests based on data.

Now our model is ready to create stories, but we still need to define an endpoint to allow our users to create a story. For this purpose, we will create a new controller.

Testing controllers

To test controllers we usually create “stubs”.

Our controller will look like this:

The class “CreateStoryService” is responsible for creating a story for the given user, and then returning the newly created story.

Note: We are assuming that the person who created this class, also wrote the proper tests for it. Once again, I will only focus on what I actually want to test and assume that the rest is safe and sound.

Let’s continue by defining all the possible scenarios for this controller:

In this case, we will not base our tests on data. If we did so, we would end up duplicating the same logic several times. Instead, we are going to stub the method like this:

Let me show you how.

If we repeat these techniques all over our app, we’ll be several steps closer to getting a well-tested and high-quality code.

Hope you liked it! Next time, we’ll look at how to test AngularJS applications, stay tuned!