As a developer, it's common practice to break down complex problems into smaller parts. When we test each part individually, it's called unit testing. This testing is typically done on the smallest parts of the application. After reading this article, you'll feel confident working with Model and Helper Specs.
Model specs are the most important test you can run in your application. They are easy to work with, mainly because, unlike controllers, there is no request/response cycle to worry about - there is nothing like rendering a template.
As a beginner, model specs are the easiest to take on and add a lot of value. They are fairly straightforward, but I'm going to share a few pointers that will help you get past the most common stumbling blocks:
{% code-block language="js" %}
describe 'Users' do
let!(:users) do
[User.create, User.create, User.create]
end
it 'uses match_array to match a scope' do
expect(User.all).to match_array(users)
end
end
{% code-block-end %}
Writing complex code will allow you to poke around in Rails internals to determine your test expectations. An easier way to do this, though, is to use a popular third-party library by thoughtbot called shoulda-matchers. This library gives you a lot of useful matchers to play with. For example; have_many, validate_presence_of.
Did you notice a slight difference there? It says “have_many” not “has_many” because it’s a matcher meant to be used with an expectation. So we would expect users to “have_many” posts. In the definition we say, has_many :posts but in our expectations, we say — we expect users to have_many orders. The same applies to validation.
Configuring shoulda-matchers is relatively easy as they have very good documentation. To start, add the shoulda-matcher gem to your gem file.
{% code-block language="js" %}
group :test do
gem 'rails-controller-testing' # If you are using Rails 5.x
gem 'rspec'
gem 'shoulda-matchers', '4.0.0.rc1'
end
{% code-block-end %}
Place the code below at the bottom of spec/rails_helper.rb
{% code-block language="js" %}
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
{% code-block-end %}
I encourage you to take a look at the shoulda-matchers documentation, there are great examples there that will guide you in writing yours.
The examples below will give you better insights on using shoulda-matchers for both your validations and association testing.
{% code-block language="js" %}
validates_uniqueness_of :username, case_sensitive: false, message: ‘Username already taken.’
validates_presence_of :username, message: ‘Username cannot be blank’
validates_presence_of :fullname, message: ‘FullName cannot be blank’
validates :username, length: { minimum: 3, maximum: 10,
too_long: ‘Maximum allowed username is 10 characters.’,
too_short: ‘Minimum allowed characters for username is 3’ }
validates :fullname, length: { minimum: 6, maximum: 20,
too_long: ‘Maximum allowed fullname is 20 characters.’,
too_short: ‘Minimum allowed characters for fullname is 6’ }
{% code-block-end %}
{% code-block language="js" %}
describe ‘Validations’ do
it do
should validate_presence_of(:username).with_message(‘Username cannot be blank’)
end
it do
should validate_length_of(:username).is_at_most(10)
.with_message(‘Maximum allowed username is 10 characters.’)
end
it { should_not validate_length_of(:username).is_at_least(2) }
it { should validate_uniqueness_of(:username).case_insensitive.with_message(‘Username already taken.’) }
end
{% code-block-end %}
{% code-block language="js" %}
describe ‘Associations’ do
it { should have_many(:opinions).with_foreign_key(:author_id) }
it { should have_many(:follows).through(:followings) }
end
{% code-block-end %}
Another useful tool for working with ActiveRecord test doubles is rspec-activemodel-mocks. This tool gives us two additional methods to use: mock-model and stub-model.
The mock-model is used to create a double of an ActiveRecord object. What's unique about it is that it already has stubbed most of the methods that are included in the ActiveRecord object, like save, create, and build, for you.
You can use 'stub-model' to stub certain behaviors. ActiveRecord test doubles become most useful when creating something that acts like an ActiveRecord object but doesn't hit the database. You can check out more in the documentation.
Using these tips and techniques, you should be able to write specs for all of your Rails app models.
Working with Rails helper specs is almost as easy as working with models. We can work with them in isolation, as there is no request/response cycle for us to worry about.
However, there is one important feature to note, and an object called helper that can be used in our examples.
Let's start by creating a spec helper file:
{% code-block language="js" %}
$ rails g rspec:helper application
{% code-block-end %}
We will define a simple method inside our helpers/application_helper.rb file for demonstration.
{% code-block language="js" %}
module ApplicationHelper
def interest_val(capital)
capital * 0.05
end
end
----------------------------------------------------------
# inside our spec/helpers/application_helper_spec.rb file
...
RSpec.describe ApplicationHelper, type: :helper do
describe "#interest_val" do
it "returns the interest on a capital" do
expect(helper.interest_val(1000)).to eq(50)
end
end
end
{% code-block-end %}
In the above example, you’ll notice how we called the method using the helper. Remember helpers are ruby modules, not ruby classes. We could have simply included the module, then had access to all the methods in our spec files.
However, using the helper has two advantages;
{% code-block language="js" %}
module ApplicationHelper
def next_page
(@page || 0) + 1
end
end
----------------------------------------------------------
# inside our spec/helpers/application_helper_spec.rb file
...
RSpec.describe ApplicationHelper, type: :helper do
describe "#next_page" do
it "returns @page plus 1" do
assign(:page, 5)
expect(helper.next_page).to eq(6)
end
end
end
{% code-block-end %}
If #next_page method doesn't recognize it, it will initialize 0 and add 1 to it. Accessing the method via the helper object is like using the technique in its proper context, instead of trying to use it abstractly out of context.
For more examples of this, see the helper spec docs.
After going through this guide, you should be more confident in conducting tests on your model and helper specs. Model specs are crucial in unit testing - a vital aspect of Test Driven Development. Unit testing guarantees that the tiniest components of your application are functioning correctly and should not be skipped. Happy coding!
To learn more about Microverse, and our supportive community of remote software developers, apply to join today!
Career advice, the latest coding trends and languages, and insights on how to land a remote job in tech, straight to your inbox.