Instantly share code, notes, and snippets.
Last active
February 11, 2022 00:23
-
Star
(1)
1
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save weedySeaDragon/af839c94ee7de016b02847b1dc1f4795 to your computer and use it in GitHub Desktop.
Using shared_context and shared_examples in Rspec with optional arguments. Answer to SO 24872199
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
#-------------------------- # | |
# | |
# @author Ashley Engelund ([email protected] weedySeaDragon @ github) | |
# @date 1/13/17 | |
# | |
# @desc This is my answer to the Stack Overflow (SO) question: | |
# How can I use a default method to create something in a shared_example, | |
# sometimes overriding it with some other method and parameters for it? | |
# | |
# @see http://stackoverflow.com/questions/24872199/rspec-with-reusable-method-for-shared-examples-context | |
# | |
# The example in the SO question refers to the classes as | |
# 'associations'. That's why those terms are used in the code. | |
# The classes in this example aren't "associations" in any way. | |
# | |
# | |
# Note that I don't think doing that is a good idea. It's a clue that | |
# your specifications need to be refactored / redesigned. | |
# It also makes your tests error prone (what if you mistype a method name?) | |
# | |
# But I did this as an exersize to show that this is all just Ruby. | |
# You can check to see if a parameter is nil and work with it just | |
# as you can in 'regular' Ruby code. | |
# | |
# Repetitive code and 'puts' are used to make the results clear | |
# and things explicit so that novices can understand it. | |
# No error checking is done; that's not the point of this example. | |
# | |
# | |
# @usage Run this with 'rspec' | |
# It will use puts statements to show which objects were created and | |
# used in the shared_examples. | |
# | |
#-------------------------- | |
#--------------------------------------------------- | |
# Classes used in the RSpec code below. | |
# | |
# The names of the classes are from the StackOverflow (SO) question. | |
# | |
# These have different numbers of attributes and so the methods used | |
# to instantiate (create) them require different numbers of arguments. | |
# I did this to make it clear that number of arguments can vary. | |
# | |
# The methods that create new objects set the attributes such that it's | |
# clear that we have used the arguments passed to the methods, and so that | |
# it's clear which method was used to create the object. | |
# | |
class AddLink | |
attr_accessor :user_symbol, :arg1 | |
def initialize(user_symbol = nil, param1 = nil) | |
@user_symbol = user_symbol | |
@arg1 = param1 | |
end | |
def self.create_and_repeat_arg1(user_symbol, param1) | |
new_link = new | |
new_link.user_symbol = "created with method '#{__method__}' #{user_symbol}" | |
new_link.arg1 = "#{param1}-#{param1}" | |
new_link | |
end | |
end | |
class DeleteLink | |
attr_accessor :user_symbol, :arg1, :arg2 | |
def self.create_and_repeat_args(user_symbol, param1, param2) | |
new_link =new | |
new_link.user_symbol = "created with method '#{__method__}' #{user_symbol}" | |
new_link.arg1 = "#{param1}-#{param1}" | |
new_link.arg2 = "#{param2}-#{param2}" | |
new_link | |
end | |
end | |
#--------------------------------------------------- | |
# Shared context and example | |
# | |
RSpec.shared_context 'assoc created with default method, parameters' do |default_class, default_method, parameters| | |
# you can use 'default_assoc' anywhere you've included this shared context | |
let(:default_assoc) { | |
default_class.send(default_method, *parameters) | |
} | |
end | |
RSpec.shared_examples 'update association service' do |optional_class, optional_method, parameters| | |
# The name of this is from the SO question | |
# warning: no error checking is done to see if default_assoc.nil? or even defined, | |
# or if optional_class.respond_to? optional_method | |
let(:assoc) { | |
if optional_class.nil? || optional_method.nil? | |
default_assoc | |
else | |
optional_class.send(optional_method, *parameters) | |
end | |
} | |
it 'used this association:' do | |
puts_example_desc_and_association(assoc) | |
end | |
end | |
#--------------------------------------------------- | |
# helper methods just to show things | |
def puts_example_desc_and_association(association) | |
puts "\n #{@__inspect_output}\n association = #{association.inspect}\n" | |
end | |
def puts_spec_and_association(association) | |
puts "\n- - -\n#{self.class.description}" | |
puts_example_desc_and_association(association) #"\n #{@__inspect_output}\n association = #{default_assoc.inspect}" | |
end | |
#--------------------------------------------------- | |
RSpec.describe 'Links' do | |
describe 'AddLink (use the default assocation, which = use the default method for creating it)' do | |
# The shared_context 'assoc created with default method, parameters' | |
# is how we set the default way to create an association within this example group. | |
# Instead of trying to memoize the class, method, and parameter, we | |
# go ahead and memoize the assocation created and call it 'default_assoc'. | |
# | |
# In other words: | |
# In this example group, the default method (and class) to create an association | |
# is DeleteLink.create_and_repeat_args('userSymbol', 'firstArg', 'secondArg', 'thirdArg') | |
# | |
# The result of this is stored in 'default_assoc' (default association). | |
# Any example that comes after this can refer to (use)' default_assoc' | |
include_context 'assoc created with default method, parameters', AddLink, :create_and_repeat_arg1, ['userSymbol', 'firstArg'] | |
# This example shows what 'default_assoc' is. | |
# If this is the first time 'default_assoc' is used, then it will | |
# execute the let(:default_assoc) block that is in the | |
# shared_context 'assoc created with default method, parameters' that is | |
# included above. | |
it 'created default association' do | |
puts_spec_and_association(default_assoc) | |
end | |
# Since no arguments are passed to this shared example, | |
# it will use 'default_assoc'. (See the code in the shared_example.) | |
it_behaves_like 'update association service' | |
end | |
describe 'DeleteLink (override default association)' do | |
# This is how 'default_assoc' is created: | |
include_context 'assoc created with default method, parameters', DeleteLink, :create_and_repeat_args, ['userSymbol', 'firstArg', 'secondArg'] | |
# Show what 'default_assoc' is: | |
it 'created default association' do | |
puts_spec_and_association(default_assoc) | |
end | |
# This is how you effectively override the default method to create an 'assocation' | |
# (or whatever class you want). | |
# | |
# In this example group, the default method (and class) to create an association | |
# is DeleteLink.create_and_repeat_args('userSymbol', 'firstArg', 'secondArg') | |
# | |
# The result of this is stored in 'default_assoc' | |
# | |
# Instead of using 'default_assoc', this will create an association | |
# with the DeleteLink class, 'new' method, and an empty argument list | |
it_behaves_like 'update association service', DeleteLink, :new, [] | |
end | |
describe "BlorfLink (use 'AddLink.new('userSymbol', 'firstArg')' to create the default association)" do | |
# This just shows that you can use the 'new' method to | |
# create the default association ('default_assoc'). | |
include_context 'assoc created with default method, parameters', AddLink, :new, ['userSymbol', 'firstArg'] | |
it 'created default association' do | |
puts_spec_and_association(default_assoc) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment