Basic TDD in Rails : Writing Validation Tests for the Model

Objective


Handle the negative case in the model unit tests.

Discussion


Why are we using unit tests for negative cases? In other words, why not write feature specs for all the negative cases?

alt text

As you can see, in the Testing Pyramid, the bottom has more tests and as we go up the pyramid, we have less tests of those type. We write unit tests to cover all cases while the integration tests are written to cover the happy path. Unit tests run fast so we can write many tests. The integration tests cuts through different layers and is slower. So we minimize the integration tests by focusing only on the happy path.

Requirements


  1. To create a valid article, it must have both title and description.

  2. If the validation fails we must display the new form with error message and allow user to correct the mistake and resubmit the form.

In this lesson we will handle the requirement #1. The next lesson will handle the requirement #2.

Steps


Step 1

Run article_spec.rb.

$ rspec spec/models/article_spec.rb 

You get 'pending' message.

Step 2

Write a test for positive case where title and description are provided.

require 'rails_helper'

describe Article, type: :model do
  it 'is valid if title and description fields have value' do
    article = Article.new(title: 'test', description: 'test')

    expect(article.save).to eq(true)
  end
end

Step 3

Run the test:

$ rspec spec/models/article_spec.rb 

The test passes without failing. This is fine. Checking the return value is one way to check we saved the article. Another way is to check the article count went up by one, like this:

require 'rails_helper'

describe Article, type: :model do
  it 'is valid if title and description fields have value' do
    expect do
      article = Article.new(title: 'test', description: 'test')
      article.save
    end.to change{Article.count}.by(1)
  end
end

Step 4

Add the second test for missing title as follows:

require 'rails_helper'

describe Article, type: :model do

  it 'is not valid if it has no title' do
    article = Article.new(description: 'test')

    expect(article.save).to be(false)
  end
end

Step 5

Run the test.

$ rspec spec/models/article_spec.rb 

It fails.

 1) Article is not valid if it has no title
     Failure/Error: expect(article.save).to be(false)

       expected false
            got true

Step 6

Add the validation for title to the article.rb as follows:

class Article < ActiveRecord::Base
  validates :title, presence: true
end

Step 7

Run the test.

$ rspec spec/models/article_spec.rb 
..

Finished in 0.05543 seconds (files took 2.1 seconds to load)
2 examples, 0 failures

It passes.

Step 8

You can also make your test precise by changing it like this:

  it 'is not valid if it has no title' do
    article = Article.new(description: 'test')
    article.save

    expect(article.errors[:title]).to eq(["can't be blank"])
  end

This check the error is due to the missing title.

Exercise


Write a failing test for missing description field and make it pass.

Summary


In this lesson you learned about the Testing Pyramid. We ran a pending spec, we wrote a tests for positive case and negative cases using unit tests. We saw two ways of asserting: checking return value vs counting the records. We also saw how to make test precise for validation checks. In the next lesson we will go up a layer and write tests for handling validation failure cases at the controller layer.


Related Articles


Create your own user feedback survey