Michael Anhari

Test-Driven Development (TDD) with Rails 5: Model Testing

Open laptop in front of a window.

Today we're going to tackle the center of our outside-in testing approach with Rails 5 by adding some validations to our Article model.

Model testing is a form of unit testing, which is used to test a single unit of our application. It's a good place to test your validations, scopes, and any instance methods you add to your model.

In more complex applications, model tests can reveal coupling in your application when you start having to instantiate or mocking objects just to test the model in consideration.

Our model test

I think requiring a title and body for each article is a reasonable requirement.

Let's start with a test that ensures an article without a title isn't considered valid by Rails.

Our model test should already exist when we used our rails generator:

# test/models/article_test.rb
require 'test_helper'

class ArticleTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

Let's replace the commented out example with our test for the title field:

# test/models/article_test.rb
require 'test_helper'

class ArticleTest < ActiveSupport::TestCase
  test "the title field is required" do
    article = Article.new(title: nil, body: "I love pajamas")

    refute article.valid?
  end
end

This is the first time we've seen refute instead of assert. This is checking to make sure the valid is false (or nil). You can also use the assert_not assertion if that reads better to you.

Let's run our test:

$ bin/rails test test/models/article_test.rb

Failure:
ArticleTest#test_the_title_field_is_required: Expected true to not be truthy.

This failure is telling us what we want. It's telling us to implement a validation for the title field. Let's implement that on our Article model:

# app/models/article.rb
class Article < ApplicationRecord
  validates :title, presence: true
end
$ bin/rails test test/models/article_test.rb

Finished in 0.044457s, 22.4936 runs/s, 22.4936 assertions/s.
1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

Perfect! We can duplicate the test / validation for the body field as well. Our new test:

# test/models/article_test.rb
require 'test_helper'

class ArticleTest < ActiveSupport::TestCase
  test "the title field is required" do
    article = Article.new(title: nil, body: "I love pajamas")

    refute article.valid?
  end

  test "the body field is required" do
    article = Article.new(title: "Pajamas: How I Feel About Them", body: "")

    refute article.valid?
  end
end
$ bin/rails test test/models/article_test.rb

Failure:
ArticleTest#test_the_body_field_is_required: Expected true to not be truthy.

Adding our validation:

# app/models/article.rb
class Article < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true
end
$ bin/rails test test/models/article_test.rb

..

Finished in 0.027840s, 71.8391 runs/s, 71.8391 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

Wrap up

That pretty much does it for this series for now! I hope you enjoyed this series. A lot of what we covered is also covered in the Rails guides testing page, but I wanted to go step by step through the failures and build the application in this series. I do encourage you to read that page as well because I could not cover everything in this series.

If there's anything else you'd like to see covered on this blog, feel free to tweet me @michaelanhari.

Cheers friend.

Newsletter

I'm working on sending out a weekly newsletter. I'll make it as easy as possible to unsubscribe at any time.