Back-end Engineering Articles

I write and talk about backend stuff like Ruby, Ruby On Rails, Databases, Testing, Architecture / Infrastructure / System Design, Cloud, DevOps, Backgroud Jobs, and more...

Twitter:
@daniel_moralesp

2019-12-27

Writing Test as TLD in Rails

On the last blog post we talked about TLD (Test Last Development), we saw some differences between TDD and TLD and what fits better if you’re working on a fast iterable project or if you’re a beginner

Now that we know more about testing strategies and principles let's write some tests. From here and now, I'll try to do everything inside a dummy project and show you how to do something there. The steps I’ll follow are:


  1. 1. Create and empty Rails project
  2. 2. Configure our test suite
  3. 3. Create a scaffold
  4. 4. Write tests


1. Create and empty Rails project

rails new blog

This will create our dummy project. You can find the project commit right here: Initial Commit · danielmoralesp/[email protected] (github.com)

2. Configure our test suite.

Now we're going to do the most important part. But first we need to have this context:

* Almost no one uses mini-test to write testing in real world Rails projects. So, if you want to know about how mini-test works, go ahead. But I highly recommend focusing on RSpec. RSpec is the library for writing tests in real world Rails projects. Spend your learning time wisely

* Almost no one uses Ruby without any framework. So don't spend time making fancy things with RSpec and Ruby. Instead spend your time learning about Rspec and Rails. I said this because a lot of tutorials focus on Ruby and RSpec, and that doesn't make any sense, since in real world projects you'll be working on Rails projects. Spend your learning time wisely

* Save these steps for the future, because this is the way you'll be always configuring your test suite. Is a repetitive process. First it will be a lot of fun, but in the 3th project it'll feel boring. Believe me

* What if we have a project running but we don't have RSpec and any test at all and we want to configure everything? These next steps change slightly, but almost all of them will work exactly the same. We’ll be talking about this in other blog post

So, let's begin. In your Gemfile you need to add rspec-rails gem

# Gemfile

group :development, :test do
  ...

  gem 'rspec-rails'
end

Now, from the console run

$ bundle install
$ rails generate rspec:install 

Now let's check our database configuration. Look for this file: /config/database.yml and you'll see this line (since we're using sqlite3 as default database)

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

# other code here

test:
  <<: *default
  database: db/test.sqlite3

This line means that the test database is going to use the default setup, with the sqlite3 adapter and that the database is going to be named as test.sqlite3. Right there is where our RSpec suite is going to save our test data, like Factories. We’ll have a blog post about Factories later

Now let's make RSpec as our default tests generator. What does this means? is simple, each time you run a rails generator, like

$ rails g model ...
$ rails g migration ...
$ rails g controller ...

These commands will create dummy tests for you in mini-test (the default test suite in Rails). As we mentioned before, we'll be using RSpec almost always in real world projects, so that files will become obsolete from the very beginning. We need to generate the same files, but in RSpec. How do we do that? Changing these lines of code

# config/application.rb

...

module Blog
  class Application < Rails::Application
    ...

    # add the following lines

    config.generators do |g|
      g.test_framework :rspec, fixture: true
      g.fixture_replacement :factory_bot, dir: 'spec/factories'
      g.view_specs false
      g.helper_specs false
    end
  end
end

With these, each time we run generators, we'll have the RSpec working for us, creating the necessary files and dummy tests

Preparing the database. 

Now that we have almost everything in place, we need to prepare our database. Let's run these commands

$ rails db:migrate 
$ rails db:test:prepare RAILS_ENV=test 
$ rails db:migrate RAILS_ENV=test 

Even we can access to our test database with this command

$ rails c -e test

At this point we have everything in place to create our first tests. Let's commit. You can see this commit here: Configuring RSpec · danielmoralesp/[email protected] (github.com)

3. Create a Scaffold

Now we're going to create some resources inside our blog post app. In this case we'll write the Article model with all of the this rails create for us

$ rails g scaffold Article title description:text
$ rails db:migrate

The lines that matter for us here are these

create      spec/models/article_spec.rb
create      spec/requests/articles_spec.rb
create      spec/routing/articles_routing_spec.rb

We can see that these files were created by the generators inside our /config/application.rb These files talk about models, requests and routing specs. You can check those files by yourself. One of them contains a lot of code, like: 

spec/requests/articles_spec.rb

The generator has created all of the specs for us. You can find the commit of this here: Article Scaffold · danielmoralesp/[email protected] (github.com)

4. Write tests

Now instead of writing our own tests, let's check what the generators wrote for us. 

Lets run this command first

$ bundle exec rspec

We can see an output like this

***.**********........

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Article add some examples to (or delete) /home/danielmorales1202/danielmorales_project/blog/spec/models/article_spec.rb
     # Not yet implemented
     # ./spec/models/article_spec.rb:4

...

The important output here are the symbols 

***.**********........

I know, it's a bit cryptic the first time you see this. But we can use another command to have a better idea about what is happening

Article
  add some examples to (or delete) /home/danielmorales1202/danielmorales_project/blog/spec/models/article_spec.rb (PENDING: Not yet implemented)

/articles
  GET /index
    renders a successful response (PENDING: Add a hash of attributes valid for your model)
  GET /show
    renders a successful response (PENDING: Add a hash of attributes valid for your model)
  GET /new
    renders a successful response
  GET /edit
    render a successful response (PENDING: Add a hash of attributes valid for your model)

Now we have a better idea about what's happening. We have pending tests and other passes now. For instance:

/articles
  GET /new
    renders a successful response

This is a successful test! and we cannot write a single line of text. The generator did all of that for us.

On the next blog post we'll go deeper on this and write our own tests

Thanks for reading
DanielM