Skip to content

Instantly share code, notes, and snippets.

@rwarbelow
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rwarbelow/121a502596b5d9bc9e04 to your computer and use it in GitHub Desktop.
Save rwarbelow/121a502596b5d9bc9e04 to your computer and use it in GitHub Desktop.
Testing Validations in Rails

Model Testing Validations in Rails (60 min)

Key Topics

During our session, we'll learn how to test model validations.

Warmup

Clone this app: git clone -b model-testing git@github.com:turingschool-examples/belibery.git.

  • Generate a migration and model for donations (use rails g model to get the model and the migration). The migration needs to have an amount and a reference to the fans table. Migrate and look at the schema.
  • Now, generate a migration that adds a string status column to the donations table. Migrate and look at the schema.
  • Open your schema, and rollback. Now try to rollback two steps. What do you see?
  • Migrate again to apply your migrations to the database.

Getting Started with Model Testing

From the RailsGuides: "In Rails, models tests are what you write to test your models."

If you use rails g model Thing, you'll have a model test file available to you within the test/models folder. This is how the repo was initially set up for Fan and Location, so you already have these two test files.

However, if you need to create a model test by hand: $ touch test/models/thing_test.rb and add this code inside of it:

require 'test_helper'

class ThingTest < ActiveSupport::TestCase
end

Remember that your generated fixtures will be loaded when you run your tests unless you remove that line in your test_helper.rb.

Testing Validations

You can find the Rails Guides validation documentation here.

Basic Validity

Let's write a test to check that a fan with all attributes is valid. Inside of test/models/fan_test.rb:

require 'test_helper'

class FanTest < ActiveSupport::TestCase
  def valid_attributes
    {
      name:               "Jorge",
      email:              "yosoybelieber@example.com",
    }
  end

  test "it creates a fan" do
    result = Fan.new(valid_attributes)

    assert result.valid?
    assert_equal "Jorge", result.name
    assert_equal "yosoybelieber@example.com", result.email
  end
end

Presence Validations

What happens if a name isn't entered? We shouldn't have a valid fan. Let's add a test. Inside of test/models/fan_test.rb:

  test "it cannot create a fan without an name" do
    result = Fan.new(email: "yosoybelieber@example.com")

    assert result.invalid?
  end

This fails because we don't have any validations for presence of a name. Inside of fan.rb, add:

  validates :name, presence: true

Uniqueness Validations

Let's assume that a fan logs into Belibery using their email address. Email addresses will need to be unique. Let's add a test:

  test "it cannot create a fan with the same email" do
    2.times { Fan.create(valid_attributes) }

    result = Fan.where(email: "yosoybelieber@example.com")
    assert_equal 1, result.count
  end

It will fail because it's creating two fans and we're asserting that there should only be one. Inside of fan.rb we need to add a validation:

  validates :email, presence: true, uniqueness: true

Format Validations

Names should only contain capital and lower case letters. Let's write a test:

  test "it only accepts letters as a name" do
    fan = Fan.create(
      name:   "Jorge1",
      email:  "yosoybelieber@example.com"
      )

    refute fan.valid?
  end

We can use regex and a format validator to make this test pass:

  validates :name,  presence:   true, 
                    format:     { with: /\A[a-zA-Z]+\z/, message: "only allows uppercase and lowercase letters"}

Length Validations

Let's limit our fans' email addresses to between 5 and 50 characters. Our test:

  test "it only accepts an email between 5 to 50 characters" do
    fan = Fan.create(
      name:               "Jorge",
      email:              "Jorj"
      )

    assert fan.invalid?
  end

We'll use the length validation to make this test pass:

  validates :email, presence:   true, 
                    uniqueness: true,
                    length:     { in: 5..50 }

Custom Validations

What happens if we want to ban all users named Richard? We will need a custom validation method. First, let's write a test:

  test "it cannot create a fan named Richard" do
    fan = Fan.create(
      name:               "Richard",
      email:              "richard@example.com"
      )

    refute fan.valid?
    assert_includes fan.errors.full_messages, "Name cannot be Richard"

  end

We can validate :no_richards with a custom validation:

  validate :no_richards

  def no_richards
    errors.add(:name, "cannot be Richard") if name == "Richard"
  end

You can also use ActiveModel::Validator for custom validations.

Inclusion and Exclusion Validations

Examples from RailsGuides:

class Coffee < ActiveRecord::Base
  validates :size, inclusion: { in: %w(small medium large),
                                message:   "%{value} is not a valid size" }
end

class Account < ActiveRecord::Base
  validates :subdomain, exclusion: { in: %w(www us ca jp),
                                     message: "%{value} is reserved." }
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment