Last active
September 22, 2020 12:16
-
-
Save ahmadhasankhan/cb0752e09827c3d24bb3d44cc12464b5 to your computer and use it in GitHub Desktop.
RSpecCheatSheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
describe 'Expectation Matchers' do | |
describe 'equivalence matchers' do | |
it 'will match loose equality with #eq' do | |
a = "2 cats" | |
b = "2 cats" | |
expect(a).to eq(b) | |
expect(a).to be == b # synonym for #eq | |
c = 17 | |
d = 17.0 | |
expect(c).to eq(d) # different types, but "close enough" | |
end | |
it 'will match value equality with #eql' do | |
a = "2 cats" | |
b = "2 cats" | |
expect(a).to eql(b) # just a little stricter | |
c = 17 | |
d = 17.0 | |
expect(c).not_to eql(d) # not the same, close doesn't count | |
end | |
it 'will match identity equality with #equal' do | |
a = "2 cats" | |
b = "2 cats" | |
expect(a).not_to equal(b) # same value, but different object | |
c = b | |
expect(b).to equal(c) # same object | |
expect(b).to be(c) # synonym for #equal | |
end | |
end | |
describe 'truthiness matchers' do | |
it 'will match true/false' do | |
expect(1 < 2).to be(true) # do not use 'be_true' | |
expect(1 > 2).to be(false) # do not use 'be_false' | |
expect('foo').not_to be(true) # the string is not exactly true | |
expect(nil).not_to be(false) # nil is not exactly false | |
expect(0).not_to be(false) # 0 is not exactly false | |
end | |
it 'will match truthy/falsey' do | |
expect(1 < 2).to be_truthy | |
expect(1 > 2).to be_falsey | |
expect('foo').to be_truthy # any value counts as true | |
expect(nil).to be_falsey # nil counts as false | |
expect(0).not_to be_falsey # but 0 is still not falsey enough | |
end | |
it 'will match nil' do | |
expect(nil).to be_nil | |
expect(nil).to be(nil) # either way works | |
expect(false).not_to be_nil # nil only, just like #nil? | |
expect(0).not_to be_nil # nil only, just like #nil? | |
end | |
end | |
describe 'numeric comparison matchers' do | |
it 'will match less than/greater than' do | |
expect(10).to be > 9 | |
expect(10).to be >= 10 | |
expect(10).to be <= 10 | |
expect(9).to be < 10 | |
end | |
it 'will match numeric ranges' do | |
expect(10).to be_between(5, 10).inclusive | |
expect(10).not_to be_between(5, 10).exclusive | |
expect(10).to be_within(1).of(11) | |
expect(5..10).to cover(9) | |
end | |
end | |
describe 'collection matchers' do | |
it 'will match arrays' do | |
array = [1,2,3] | |
expect(array).to include(3) | |
expect(array).to include(1,3) | |
expect(array).to start_with(1) | |
expect(array).to end_with(3) | |
expect(array).to match_array([3,2,1]) | |
expect(array).not_to match_array([1,2]) | |
expect(array).to contain_exactly(3,2,1) # similar to match_array | |
expect(array).not_to contain_exactly(1,2) # but use individual args | |
end | |
it 'will match strings' do | |
string = 'some string' | |
expect(string).to include('ring') | |
expect(string).to include('so', 'ring') | |
expect(string).to start_with('so') | |
expect(string).to end_with('ring') | |
end | |
it 'will match hashes' do | |
hash = {:a => 1, :b => 2, :c => 3} | |
expect(hash).to include(:a) | |
expect(hash).to include(:a => 1) | |
expect(hash).to include(:a => 1, :c => 3) | |
expect(hash).to include({:a => 1, :c => 3}) | |
expect(hash).not_to include({'a' => 1, 'c' => 3}) | |
end | |
end | |
describe 'other useful matchers' do | |
it 'will match strings with a regex' do | |
# This matcher is a good way to "spot check" strings | |
string = 'The order has been received.' | |
expect(string).to match(/order(.+)received/) | |
expect('123').to match(/\d{3}/) | |
expect(123).not_to match(/\d{3}/) # only works with strings | |
email = 'someone@somewhere.com' | |
expect(email).to match(/\A\w+@\w+\.\w{3}\Z/) | |
end | |
it 'will match object types' do | |
expect('test').to be_instance_of(String) | |
expect('test').to be_an_instance_of(String) # alias of #be_instance_of | |
expect('test').to be_kind_of(String) | |
expect('test').to be_a_kind_of(String) # alias of #be_kind_of | |
expect('test').to be_a(String) # alias of #be_kind_of | |
expect([1,2,3]).to be_an(Array) # alias of #be_kind_of | |
end | |
it 'will match objects with #respond_to' do | |
string = 'test' | |
expect(string).to respond_to(:length) | |
expect(string).not_to respond_to(:sort) | |
end | |
it 'will match class instances with #have_attributes' do | |
class Car | |
attr_accessor :make, :year, :color | |
end | |
car = Car.new | |
car.make = 'Dodge' | |
car.year = 2010 | |
car.color = 'green' | |
expect(car).to have_attributes(:color => 'green') | |
expect(car).to have_attributes( | |
:make => 'Dodge', :year => 2010, :color => 'green' | |
) | |
end | |
it 'will match anything with #satisfy' do | |
# This is the most flexible matcher | |
expect(10).to satisfy do |value| | |
(value >= 5) && (value <=10) && (value % 2 == 0) | |
end | |
end | |
end | |
describe 'predicate matchers' do | |
it 'will match be_* to custom methods ending in ?' do | |
# drops "be_", adds "?" to end, calls method on object | |
# Can use these when methods end in "?", require no arguments, | |
# and return true/false. | |
# with built-in methods | |
expect([]).to be_empty # [].empty? | |
expect(1).to be_integer # 1.integer? | |
expect(0).to be_zero # 0.zero? | |
expect(1).to be_nonzero # 1.nonzero? | |
expect(1).to be_odd # 1.odd? | |
expect(2).to be_even # 1.even? | |
# be_nil is actually an example of this too | |
# with custom methods | |
class Product | |
def visible?; true; end | |
end | |
product = Product.new | |
expect(product).to be_visible # product.visible? | |
expect(product.visible?).to be true # exactly the same as this | |
end | |
it 'will match have_* to custom methods like has_*?' do | |
# changes "have_" to "has_", adds "?" to end, calls method on object | |
# Can use these when methods start with "has_", end in "?", | |
# and return true/false. Can have arguments, but not required. | |
# with built-in methods | |
hash = {:a => 1, :b => 2} | |
expect(hash).to have_key(:a) # hash.has_key? | |
expect(hash).to have_value(2) # hash.has_value? | |
# with custom methods | |
class Customer | |
def has_pending_order?; true; end | |
end | |
customer = Customer.new | |
expect(customer).to have_pending_order # customer.has_pending_order? | |
expect(customer.has_pending_order?).to be true # same as this | |
end | |
end | |
describe 'observation matchers' do | |
# Note that all of these use "expect {}", not "expect()". | |
# It is a special block format that allows a | |
# process to take place inside of the expectation. | |
it 'will match when events change object attributes' do | |
# calls the test before the block, | |
# then again after the block | |
array = [] | |
expect { array << 1 }.to change(array, :empty?).from(true).to(false) | |
class WebsiteHits | |
attr_accessor :count | |
def initialize; @count = 0; end | |
def increment; @count += 1; end | |
end | |
hits = WebsiteHits.new | |
expect { hits.increment }.to change(hits, :count).from(0).to(1) | |
end | |
it 'will match when events change any values' do | |
# calls the test before the block, | |
# then again after the block | |
# notice the "{}" after "change", | |
# can be used on simple variables | |
x = 10 | |
expect { x += 1 }.to change {x}.from(10).to(11) | |
expect { x += 1 }.to change {x}.by(1) | |
expect { x += 1 }.to change {x}.by_at_least(1) | |
expect { x += 1 }.to change {x}.by_at_most(1) | |
# notice the "{}" after "change", | |
# can contain any block of code | |
z = 11 | |
expect { z += 1 }.to change { z % 3 }.from(2).to(0) | |
# Must have a value before the block | |
# Must change the value inside the block | |
end | |
it 'will match when errors are raised' do | |
# observes any errors raised by the block | |
expect { raise StandardError }.to raise_error | |
expect { raise StandardError }.to raise_exception | |
expect { 1 / 0 }.to raise_error(ZeroDivisionError) | |
expect { 1 / 0 }.to raise_error.with_message("divided by 0") | |
expect { 1 / 0 }.to raise_error.with_message(/divided/) | |
# Note that the negative form does | |
# not accept arguments | |
expect { 1 / 1 }.not_to raise_error | |
end | |
it 'will match when output is generated' do | |
# observes output sent to $stdout or $stderr | |
expect { print('hello') }.to output.to_stdout | |
expect { print('hello') }.to output('hello').to_stdout | |
expect { print('hello') }.to output(/ll/).to_stdout | |
expect { warn('problem') }.to output(/problem/).to_stderr | |
end | |
end | |
describe 'compound expectations' do | |
it 'will match using: and, or, &, |' do | |
expect([1,2,3,4]).to start_with(1).and end_with(4) | |
expect([1,2,3,4]).to start_with(1) & include(2) | |
expect(10 * 10).to be_odd.or be > 50 | |
array = ['hello', 'goodbye'].shuffle | |
expect(array.first).to eq("hello") | eq("goodbye") | |
end | |
end | |
describe 'composing matchers' do | |
# some matchers accept matchers as arguments. (new in rspec3) | |
it 'will match all collection elements using a matcher' do | |
array = [1,2,3] | |
expect(array).to all( be < 5 ) | |
end | |
it 'will match by sending matchers as arguments to matchers' do | |
string = "hello" | |
expect { string = "goodbye" }.to change { string }. | |
from( match(/ll/) ).to( match(/oo/) ) | |
hash = {:a => 1, :b => 2, :c => 3} | |
expect(hash).to include(:a => be_odd, :b => be_even, :c => be_odd) | |
expect(hash).to include(:a => be > 0, :b => be_within(2).of(4)) | |
end | |
it 'will match using noun-phrase aliases for matchers' do | |
# These are built-in aliases that make | |
# specs read better by using noun-based | |
# phrases instead of verb-based phrases. | |
# valid but awkward example | |
fruits = ['apple', 'banana', 'cherry'] | |
expect(fruits).to start_with( start_with('a') ) & | |
include( match(/a.a.a/) ) & | |
end_with( end_with('y') ) | |
# improved version of the previous example | |
# "start_with" becomes "a_string_starting_with" | |
# "end_with" becomes "a_string_ending_with" | |
# "match" becomes "a_string_matching" | |
fruits = ['apple', 'banana', 'cherry'] | |
expect(fruits).to start_with( a_string_starting_with('a') ) & | |
include( a_string_matching(/a.a.a/) ) & | |
end_with( a_string_ending_with('y') ) | |
# valid but awkward example | |
array = [1,2,3,4] | |
expect(array).to start_with( be <= 2 ) | | |
end_with( be_within(1).of(5) ) | |
# improved version of the previous example | |
# "be <= 2" becomes "a_value <= 2" | |
# "be_within" becomes "a_value_within" | |
array = [1,2,3,4] | |
expect(array).to start_with( a_value <= 2 ) | | |
end_with( a_value_within(1).of(5) ) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rails_helper' | |
RSpec.describe TodosController, :type => :controller do | |
describe "GET #index" do | |
#describe "POST #create" do | |
#describe "GET #show" do | |
#describe "PATCH #update" do (or PUT #update) | |
#describe "DELETE #destroy" do | |
#describe "GET #new" do | |
#describe "GET #edit" do | |
# NORMALLY, you DO NOT want render_views, or you only want to call it in | |
# a single context. | |
# More on render_views: | |
# https://www.relishapp.com/rspec/rspec-rails/v/3-1/docs/controller-specs/render-views | |
render_views # ONLY have this if you're certain you need it | |
it "reads like a sentence (almost)" do | |
# Available HTTP methods: post, get, patch, put, delete, head | |
get :index | |
params = { id: 123 } | |
get :edit, params # old non-kwarg style | |
get :edit, params: params # new kwarg style | |
params = { widget: { description: 'Hello World' } } | |
params.merge!(format: :js) # Specify format for AJAX/JS responses (e.g. create.js.erb view) | |
post :create, params # old non-kwarg style | |
post :create, params: params # new kwarg style | |
# All optional kwargs: | |
post :create, | |
params: {}, # hash with HTTP parameters, may be nil | |
body: "...", # request body string, appropriately encoded (application/x-www-form-urlencoded or multipart/form-data) | |
session: {}, # hash of parameters to store in session, may be nil. | |
flash: {}, # hash of parameters to store in flash, may be nil. | |
format: :json, # Request format (string or symbol), defaults to nil. | |
as: :json # Content type must be symbol that corresponds to a mime type, defaults to nil. | |
# Testing 404s in controllers (assuming default Rails handling of RecordNotFound) | |
expect { delete :destroy, { id: 'unknown' } }.to raise_error(ActiveRecord::RecordNotFound) | |
# Rails `:symbolized` status codes at end of each status code page at http://httpstatus.es/ | |
expect(response).to have_http_status(:success) # 200 | |
expect(response).to have_http_status(:forbidden) # 403 | |
expect(response).to redirect_to foo_path | |
expect(response).to render_template(:template_filename_without_extension) | |
expect(response).to render_template(:destroy) | |
# Need response.body? Requires render_views call outside "it" block (see above & read given URL) | |
expect(response.body).to match /Bestsellers/ | |
expect(response.body).to include "Bestsellers" | |
expect(response.headers["Content-Type"]).to eq "text/html; charset=utf-8" | |
expect(response.headers["Content-Type"]).to eq "text/javascript; charset=utf-8" | |
# assigns(:foobar) accesses the @foobar instance variable | |
# the controller method made available to the view | |
# Think of assigns(:widgets) as @widgets in the controller method | |
expect(assigns(:widgets)).to eq([widget1, widget2, widget3]) | |
# Think of assigns(:product) as @product in the controller method | |
expect(assigns(:product)).to eq(bestseller) | |
expect(assigns(:cat)).to be_cool # cat.cool is a boolean, google "rspec predicate matchers" | |
expect(assigns(:employee)).to be_a_new(Employee) | |
# Asserting flash messages | |
expect(flash[:notice]).to eq "Congratulations on buying our stuff!" | |
expect(flash[:error]).to eq "Buying our stuff failed :-(" | |
expect(flash[:alert]).to eq "You didn't buy any of our stuff!!!" | |
# Query the db to assert changes persisted | |
expect(Invoice.count).to eq(1) | |
# Reload from db an object fetched in test setup when its record in db | |
# is updated by controller method, otherwise you're testing stale data | |
employee.reload | |
invoice.reload | |
product.reload | |
widget.reload | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment