Beginners introduction to testing Ruby On Rails application with RSpec and Capybara.
Install rails without
minitest:rails new rails-rspec-tutorial -T;Add
rspectoGemfile:
group:development,:testdogem'rspec-rails','~> 3.7'endInstall dependencies:
bundle install;Execute
bundle exec rails generate rspec:installto create a/specdir;Run rspec:
bundle exec rspec
- Create first spec:
require"rails_helper"RSpec.describe"hello spec"dodescribe"math"doexpect(6 * 7).toeq(43)endend- And catch an error:
Failures:
1) hello spec math should be able to perform basic math
Failure/Error: expect(6 * 7).to eq(43)
expected: 43
got: 42
- Fix the error to get test passed:
require"rails_helper"RSpec.describe"hello spec"dodescribe"math"doit"should be able to perform basic math"do# expect(6 * 7).to eq(43) # => falseexpect(6 * 7).toeq(42)endendend- Add another spec with empty string:
require"rails_helper"RSpec.describe"hello spec"do# ...describeStringdolet(:string){String.new}it"should provide an empty string"doexpect(string).toeq("")endendend- Create model
Article:
bundle exec rails g model Article title:string body:text active:boolean
RAILS_ENV=test bundle exec rake db:migrate
- Create
spec/models/article_spec.rb:
require"rails_helper"RSpec.describeArticle,type: :modeldocontext"validations tests"doit"ensures the title is present"doarticle=Article.new(body: "Content of the body")expect(article.valid?).toeq(false)endit"ensures the body is present"doarticle=Article.new(title: "Title")expect(article.valid?).toeq(false)endit"ensures the article is active by default"doarticle=Article.new(body: "Content of the body",title: "Title")expect(article.active?).toeq(true)endit"should be able to save article"doarticle=Article.new(body: "Content of the body",title: "Title")expect(article.save).toeq(true)endendcontext"scopes tests"doendend- Add some presence validators to
app/models/article.rb:
classArticle < ApplicationRecordvalidates_presence_of:title,:bodyend- Create a migration:
classMakeArticleActiveByDefault < ActiveRecord::Migration[5.1]defchangechange_column:articles,:active,:boolean,default: trueendend- Run migration:
bundle exec rake db:migrate
- Add scope specs:
require"rails_helper"RSpec.describeArticle,type: :modeldo# ...context"scopes tests"dolet(:params){{body: "Content of the body",title: "Title",active: true}}before(:each)doArticle.create(params)Article.create(params)Article.create(params)Article.create(params.merge(active: false))Article.create(params.merge(active: false))endit"should return all active articles"doexpect(Article.active.count).toeq(3)endit"should return all inactive articles"doexpect(Article.inactive.count).toeq(2)endendend- Create Articles controller scaffold:
bundle exec rails g scaffold_controller Articles
rm -rf spec/views spec/routing spec/request spec/helpers spec/requests
- Create Articles spec:
require"rails_helper"RSpec.describeArticlesController,type: :controllerdocontext"GET #index"doit"returns a success response"doget:index# expect(response.success).to eq(true)expect(response).tobe_successendendcontext"GET #show"dolet!(:article){Article.create(title: "Test title",body: "Test body")}it"returns a success response"doget:show,params: {id: article}expect(response).tobe_successendendend- Add articles to
routes.rbfile:
Rails.application.routes.drawdoresources:articlesend- Add
--format documentationto.rspec
- Add Capybara to
Gemfile:
group:development,:testdo# Call 'byebug' anywhere in the code to stop execution and get a debugger consolegem'byebug',platforms: [:mri,:mingw,:x64_mingw]gem'rspec-rails','~> 3.7'gem'capybara'end- Add these lines to your
spec/rails_helper.rbfile:
require'capybara/rails'require'capybara/rspec'- Create first feature test by running
bundle exec rails g rspec:feature home_page:
require"rails_helper"RSpec.feature"Visiting the homepage",type: :featuredoscenario"The visitor should see a welcome message"dovisitroot_pathexpect(page).tohave_text("Welcome to my blog!")endend- Add
roottoroutes.rb:
Rails.application.routes.drawdoroot"home#index"resources:articlesend- Generate controller:
bundleexecrailsgcontrollerHomeindexrm -rfspec/controllers/home_controller_spec.rbspec/views/homespec/views/home/index.html.erb_spec.rbspec/helpers/home_helper_spec.rb- Modify the view
app/views/home/index.html.erb:
<h1>Welcome to my blog!</h1><p>Find me in app/views/home/index.html.erb</p>bundle exec rails g rspec:feature articlesCreate feature spec:
require'rails_helper'RSpec.feature"Articles",type: :featuredocontext"Create new article"doscenario"Should be successful"dovisitnew_article_pathwithin("form")dofill_in"Title",with: "Test title"fill_in"Body",with: "Test body"check"Active"endclick_button"Create Article"expect(page).tohave_content("Article successfully created")endscenario"Should fail"doendendcontext"Update article"doendcontext"Remove existing article"doendend- Fix the view (
app/views/articles/_form.erb):
<%= form_with(model: article, local: true) do |form| %><% if article.errors.any? %><divid="error_explanation"><h2><%=pluralize(article.errors.count,"error")%> prohibited this article from being saved:</h2><ul><%article.errors.full_messages.eachdo |message| %><li><%=message%></li><%end%></ul></div><%end%><div><%=form.label:title%><%=form.text_field:title,id: "article_title"%></div><div><%=form.label:body%><%=form.text_area:body,id: "article_body"%></div><div><%=form.label:active%><%=form.check_box:active,id: "article_active"%></div><divclass="actions"><%=form.submit%></div><%end%>- Allow attributes in
app/controllers/articles_controller.rb:
defarticle_paramsparams.require(:article).permit(:active,:id,:title,:body)end- Full Articles test:
require'rails_helper'RSpec.feature"Articles",type: :featuredocontext"Create new article"dobefore(:each)dovisitnew_article_pathwithin("form")dofill_in"Title",with: "Test title"check"Active"endendscenario"should be successful"dofill_in"Body",with: "Test body"click_button"Create Article"expect(page).tohave_content("Article was successfully created")endscenario"should fail"doclick_button"Create Article"expect(page).tohave_content("Body can't be blank")endendcontext"Update article"dolet(:article){Article.create(title: "Test title",body: "Test content")}before(:each)dovisitedit_article_path(article)endscenario"should be successful"dowithin("form")dofill_in"Body",with: "New body content"endclick_button"Update Article"expect(page).tohave_content("Article was successfully updated")endscenario"should fail"dowithin("form")dofill_in"Body",with: ""endclick_button"Update Article"expect(page).tohave_content("Body can't be blank")endendcontext"Remove existing article"dolet!(:article){Article.create(title: "Test title",body: "Test content")}scenario"remove article"dovisitarticles_pathclick_link"Destroy"expect(page).tohave_content("Article was successfully destroyed")expect(Article.count).toeq(0)endendend- Add to
Gemfile:
group:development,:testdo# Call 'byebug' anywhere in the code to stop execution and get a debugger consolegem'byebug',platforms: [:mri,:mingw,:x64_mingw]gem'rspec-rails','~> 3.7'gem'capybara'gem'selenium-webdriver'end- Add to
spec/rspec_helper.rb:
Capybara.default_driver=:selenium_chrome_headless- Fix articles spec:
context"Remove existing article"dolet!(:article){Article.create(title: "Test title",body: "Test content")}scenario"remove article"dovisitarticles_pathexpect(Article.count).toeq(1)accept_alertdoclick_link"Destroy"endexpect(page).tohave_content("Article was successfully destroyed")expect(Article.count).toeq(0)endendRails coverage report:
bundle exec rails statsTo install codecov add to your
Gemfile:
gem'simplecov',require: false,group: :test- Add to your
spec/rails_helper.rb:
require'simplecov'SimpleCov.start- Don't forget to add
coverage/dir to your.gitignorefile;