MiniTest

In this article, I would like to give a brief introduction to Rails minitest.

https://media1.giphy.com/media/xT1XGWGd90BrYwnTl6/giphy.gif?cid=ecf05e477bda12oe5lmzz3mbgue2r550pa6o1zvav5redmbb&rid=giphy.gif Minitest is a testing tool for Ruby that provides a complete suite of testing facilities. It also supports behaviour-driven development, mocking and benchmarking. With the release of Ruby 1.9, it was added to Ruby's standard library, which increased its popularity. MiniTest was woven into the Rails fabric. Rails generates a testing skeleton when the user generates a model. These tests are held within the testing folder, and the folder contains testing structure for helpers, mailers, models and others. The purpose of writing test is to ensure that our code adheres to desired functionality during refactoring.

Today we are going to show examples of Model testing. Inside of a rails new project lets create a model

Every rails application has three environments: development, test and production. We can set the environment to test by running ‘rails db:migrate Rails _ENV=test’

rails g model book title:string author:string year_published:integer genre:string

https://media3.giphy.com/media/l4Ep6yR3qYcSW5v7q/giphy.gif?cid=ecf05e476qjxwfsrinpovh6ax38z4j2250ry036s9ku260pd&rid=giphy.gif

Fixtures

Before we write any code we have to setup our fixtures. Navigate to fixtures within the test folder, here we can set up testing data or an example of our model. We should see a .yml file two examples would be created for us but we can create our own. Make sure when you write your fixtures that the obj key is aligned properly and the following key/value pairs aligned also.

valid:
   title: The story of Joe Blow
   author: Jim Blow
   year_published: 2003
   genre: Biography

Now it's time to write our first test. Navigate to the book_test.rb file and notice that our class inherits from the ActiveSupport::TestCase not MiniTest, well MiniTest is a super class of the TestCase class. All of our tests must be written within a class inheriting from CaseTest. First thing first let's write our setup method. This method is just to declare an instance method we can work with.

def setup
  # Our instance variable comes from the books.yml file and the name of our example we want to use is passed in.

    @book = books(:valid)
  end

Assertions

You can notice that there's a test function commented out. Lets uncomment this method and break down what it means. We can write our own after breaking this one down.

test "the truth" do
    assert true
  end

First the ‘test’ keyword, because the books_test is a ruby file we can just write ruby within the file that's how we wrote our setup method but within the CaseTest class we have access to the test keyword which declares a test. Following the test keyword we have our test name, there's little restriction of the test name.

Walking into the test we see another keyword the assert, an assertion is a line of code that evaluates an object for expected results. In Other words an assert checks if the test is true, if the expression following the assert keyword is false then the test fails.

Let's try to run our example. To run a single test we have to tell rails to go to the file and run the line number we specify.

rails test test/models/book_test.rb:13

We can also run all of our tests, a whole directory or a single test file. Rails test has a lot of options, check them out sometime with rails test -h.

If you don’t have any errors with your yml file we should get 1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

Now let's change the true to false and fail this test. rails test test/models/book_test.rb:13

And check out what we get.

Failure:
BookTest#test_the_truth [/Users/keenanjones/Desktop/Tutorials/rails_testing/test/models/book_test.rb:14]:
Expected false to be truthy.

We can change the message to something more readable, every assertion has an optional message parameter that we can use just by adding a , ‘simple message’

test "the truth" do
    assert false, 'simple message'
  end

The test still fails but we have our message in the failure.

Switch the assertion back to true so we can make sure all the tests pass before moving on.

There's other assertion keywords besides assert. Refute is the opposite of assert, if the expression following the refute keyword is true the test will fail.

Okay these are our assertions for today. In another tab open the book model file book.rb we’re going to deal with this later but right now let’s write our first test.

 test 'valid book' do
    assert @book.valid?
  end

This test is just to check if our book instance is a valid book, now this test should pass because we don’t have any validations or requirements for the book class…yet

So breaking this down the @book.valid will equal true and the assertion will pass.

Now our challenge is to make sure that our @book is invalid without a title,

test 'invalid without title' do
    @book.title = nil
    refute @book.valid?, 'saved book without a title'
  
    assert_not_nil @book.errors[:title], 'no validation error for title present'
  end

If we comment the refute line and run the code we see that the test passed because our @book.error[:title] equals an empty array. We want this line for many reasons but in most cases just for backup for our refute case. Now finally we will get this test to pass.

In order to do this we need to navigate to the book.rb file to create a validation, to make sure that the title is always present.

validates :title, presence: true

And there you go, you just wrote your first Rails miniTest. Feel free to play around with the different assertions keywords.

Blog

copyright©2021 Keenan all rights reserved