-
Notifications
You must be signed in to change notification settings - Fork 0
# Step 13 — Rspec test Setup
Gerald Goh edited this page Jul 26, 2020
·
12 revisions
Ensure rspec-rails present in both the :development and :test groups of your app’s Gemfile:
/gemfile
group :development, :test do
gem 'rspec-rails', '~> 4.0'
end
In app root directory run the following command in terminal
# Download and install
$ bundle update
$ bundle install
$ bundle update rspec-rails
# Generate boilerplate configuration files
# (check the comments in each generated file for more information)
$ rails generate rspec:install
create .rspec
create spec
create spec/spec_helper.rb
create spec/rails_helper.rb
Create boilerplate specs with rails generate after coding is complete
$ rails generate rspec:model user
/rspec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
let! (:users) { create_list(:user, 10) }
it { should have_many(:readings) }
it { should validate_presence_of(:name) }
it { should validate_length_of(:name) }
it { should validate_presence_of(:email) }
it { should validate_uniqueness_of(:email) }
it { should validate_presence_of(:password) }
it { should validate_length_of(:password) }
it { should validate_confirmation_of(:password) }
it { should validate_presence_of(:units) }
it { should validate_presence_of(:target) }
end
$ bundle exec rspec spec/models
.......
Finished in 0.26756 seconds (files took 2.63 seconds to load)
7 examples, 0 failures
$ rails generate rspec:model reading
create spec/models/reading_spec.rb
require 'rails_helper'
RSpec.describe Reading, type: :model do
let!(:user) { create(:user) }
let!(:readings) { create_list(:reading, 1, user_id: user.id) }
let(:user_id) { user.id }
let(:id) { readings.first.id }
it { should belong_to(:user) }
it { should validate_presence_of(:bedroom) }
it { should validate_presence_of(:study) }
it { should validate_presence_of(:garage) }
it { should validate_presence_of(:living) }
it { should validate_presence_of(:kitchen) }
it { should validate_presence_of(:living) }
it { should validate_presence_of(:available) }
it { should validate_presence_of(:saved) }
end
$ bundle exec rspec spec/models/reading_spec.rb
.........
Finished in 1.4 seconds (files took 2.95 seconds to load)
9 examples, 0 failures
/gemfile
group :test do
gem 'database_cleaner'
gem 'factory_bot_rails'
gem 'shoulda-matchers'
end
spec/support/request_spec_helper.rb
module RequestSpecHelper
# Parse JSON response to ruby hash
def json
JSON.parse(response.body)
end
end
spec/railes_helper.rb
# require database cleaner at the top level
require 'database_cleaner'
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort('The Rails environment is running in production mode!') if Rails.env.production?
require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point!
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
# run as spec files by default. This means that files in spec/support that end
# in _spec.rb will both be required and run as specs, causing the specs to be
# run twice. It is recommended that you do not name files matching this glob to
# end with _spec.rb. You can configure this pattern with the --pattern
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
#
# The following line is provided for convenience purposes. It has the downside
# of increasing the boot-up time by auto-requiring all files in the support
# directory. Alternatively, in the individual `*_spec.rb` files, manually
# require only the support files necessary.
#
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
config.include RequestSpecHelper, type: :request
# add `FactoryBot` methods
config.include FactoryBot::Syntax::Methods
# start by truncating all the tables but then use the faster transaction strategy the rest of the time.
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
DatabaseCleaner.strategy = :transaction
end
# start the transaction strategy as examples are run
config.around(:each) do |example|
DatabaseCleaner.cleaning do
example.run
end
end
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# RSpec Rails can automatically mix in different behaviours to your tests
# based on their file location, for example enabling you to call `get` and
# `post` in specs under `spec/controllers`.
#
# You can disable this behaviour by removing the line below, and instead
# explicitly tag your specs with their type, e.g.:
#
# RSpec.describe UsersController, :type => :controller do
# # ...
# end
#
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
# configure shoulda matchers to use rspec as the test framework and full matcher libraries for rails
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
$ rails g rspec:controller users
create spec/requests/users_request_spec.rb
spec/factories/users.rb
FactoryBot.define do
factory :user do
name { Faker::Name.name }
email { Faker::Internet.email }
password { "somepassword" }
password_confirmation { "somepassword"}
units { 1800 }
target { 5 }
end
end
spec/requests/user_request_spec.rb
require 'rails_helper'
RSpec.describe "Users", type: :request do
let! (:users) { create_list(:user, 15) }
let (:user_id) { users.last.id }
describe 'Get /api/v1/users' do
before { get '/api/v1/users' }
it 'return all users' do
expect(json).not_to be_empty
expect(json["data"].size).to eq(15)
end
it 'return status code 200' do
expect(response).to have_http_status(200)
end
end
describe 'GET /api/v1/users/:id' do
before { get "/api/v1/users/#{user_id}" }
context 'when user exists' do
it 'resturns the user' do
expect(json).not_to be_empty
expect(json["data"]["id"]).to eq(user_id)
end
it 'returns status code 200' do
expect(response).to have_http_status(200)
end
end
end
context 'when user does not exist' do
let(:user_id) { 9 }
it 'returns nil' do
expect(response).to eq(nil)
end
end
end
describe 'POST /api/v1/users' do
let! (:valid_attributes) { {user: { name: 'Bruce tester', email: 'tester@email.com', password: 'password', password_confirmation: 'password', units: 1800, target: 5 } } }
context 'when the request is valid' do
before { post '/api/v1/users', params: valid_attributes }
it 'returns status code 200 when request is accepted' do
expect(response).to have_http_status(200)
end
end
context 'when the request is invalid' do
before { post '/api/v1/users', params: { user: { name: ' ', email: ' ', password: 'password', password_confirmation: 'password', units: 1800, target: 5 } } }
it 'return status code 400' do
expect(json["code"]).to eq(400)
end
it 'returns a validation error message' do
expect(response.body).to match(/can't be blank/)
end
end
end
spec/factories/reading.rb
FactoryBot.define do
factory :reading do
bedroom { Faker::Number.between(from: 1, to: 10) }
study { Faker::Number.between(from: 1, to: 10) }
garage { Faker::Number.between(from: 1, to: 10) }
living { Faker::Number.between(from: 1, to: 10) }
kitchen { Faker::Number.between(from: 1, to: 10) }
guest { Faker::Number.between(from: 1, to: 10) }
consumption { Faker::Number.between(from: 1, to: 10) }
available { Faker::Number.between(from: 1, to: 10) }
saved { Faker::Number.between(from: 1, to: 10) }
user_id { nil }
end
end
$ rails g rspec:controller readings
create spec/requests/readings_request_spec.rb
spec/requests/readings_requests_spec.rb
require 'rails_helper'
RSpec.describe "Readings", type: :request do
let!(:user) { create(:user) }
let!(:readings) { create_list(:reading, 2, user_id: user.id) }
let(:user_id) { user.id }
let(:id) { readings.first.id }
describe 'POST /api/v1/readings' do
let! (:valid_attributes) { { bedroom: 1, study: 1, garage: 1, living: 1, kitchen: 1, guest: 1, consumption: 1, available: 1, saved: 1, user_id: 1 } }
context 'when the readings creation' do
before { post '/api/v1/readings', params: valid_attributes }
it 'returns error code 401 before login' do
expect(json["code"]).to eq(401)
end
end
context 'when the request is invalid' do
before { post '/api/v1/readings', params: { bedroom: ' ', study: ' ', garage: 1, living: 1, kitchen: 1, guest: 1, consumption: 1, available: 1, saved: 1, user_id: 1 } }
it 'return status code 401 before login' do
expect(json["code"]).to eq(401)
end
end
end
describe 'GET /api/v1/readings' do
before { get "/api/v1/readings" }
context 'when all readings are hidden before required login' do
it 'returns error code 401' do
expect(json["code"]).to eq(401)
end
end
end
describe 'GET /api/v1/readings/user/:user_id' do
before { get "/api/v1/readings/user/#{user_id}" }
context 'when users readings are hidden before required login' do
it 'returns error code 401' do
expect(json["code"]).to eq(401)
end
end
context 'when user does not exists' do
let(:user_id) { 0 }
it 'returns status code 401' do
expect(json["code"]).to eql(401)
end
end
end
describe 'GET /api/v1/user/:user_id/reading/:id' do
before { get "/api/v1/user/#{user_id}/reading/#{id}" }
context 'when user single reading is hidden before required login' do
it 'returns status code 401' do
expect(json["code"]).to eq(401)
end
context 'when user does not exists' do
let(:user_id) { 0 }
it 'returns status code 401' do
expect(json["code"]).to eql(401)
end
end
end
end
end