Simple Minitest helper to replace values such as an instance variable of an object or an environment variable for the duration of a block or a group of tests.
This comes in very handy when you have to deviate from default configuration in order to test some aspects of your code.
- Homepage
- API
- Author: Sven Schwyn - Bitcetera
Thank you for supporting free and open-source software by sponsoring on GitHub or on Donorbox. Any gesture is appreciated, from a single Euro for a ☕️ cup of coffee to 🍹 early retirement.
Add the following to the Gemfile or gems.rb of your Bundler powered Ruby project:
gem 'minitest-substitute'And then install the bundle:
bundle install
Finally, require this gem in your test_helper.rb or spec_helper.rb:
require 'minitest/substitute'Rails 7 has polluted Object for everybody by introducing Object#with. To prevent collisions, Minitest::Substitute has switched from with to substitute as of version 1.0.0.
After having updated this gem, you'll have to adapt all your tests accordingly:
# Version 0.x.x
with '@version', 2, on: config do
config.instance_variable_get('@version') # => 2
end
# Version 1.x.x
substitute '@version', 2, on: config do
config.instance_variable_get('@version') # => 2
endTo substitute the value of an instance variable for the duration of a block:
class Config
def initialize
@version = 1
end
end
config = Config.new
config.instance_variable_get('@version') # => 1
substitute '@version', 2, on: config do
config.instance_variable_get('@version') # => 2
end
config.instance_variable_get('@version') # => 1on is set explicitly in this case. If you omit this argument, self will be used as target by default.
Class variables can be substituted as well:
class Config
@@counter = 0
end
Config.class_variable_get('@@counter') # => 0
substitute '@@counter', 42, on: Config do
Config.class_variable_get('@@counter') # => 42
end
Config.class_variable_get('@@counter') # => 0Same goes for global variables:
$verbose = false # => false
substitute '$verbose', true do
$verbose # => true
end
$verbose # => falseAnd it works for globals like ENV as well which comes in handy when you have to temporarily override the value of an environment variable:
ENV['EDITOR'] # => 'vi'
substitute "ENV['EDITOR']", 'nano' do
ENV['EDITOR'] # => 'nano'
end
ENV['EDITOR'] # => 'vi'You can even substitute constants, however, you have to use their absolute name starting with :::
module Animals
DOG_MAKES = 'woof'
CAT_MAKES = 'meow'
end
Animals::DOG_MAKES # => 'woof'
substitute '::Animals::DOG_MAKES', Animals::CAT_MAKES do
Animals::DOG_MAKES # => 'meow'
end
Animals::DOG_MAKES # => 'woof'Remember that class declarations are assigned to constants as well:
class Dog
self.makes
'woof'
end
end
class Cat
self.makes
'meow'
end
end
Dog.makes # => 'woof'
substitute '::Dog', Cat do
Dog.makes # => 'meow'
end
Dog.makes # => 'woof'It's safe to nest multiple substitute statements.
When using spec notation, you can change a value for all tests within a describe group:
class Config
def initialize
@version = 1
end
end
describe Config do
subject do
Config.new
end
describe 'original version' do
it "returns the original version" do
_(subject.instance_variable_get('@version')).must_equal 1
end
end
describe 'sustituted version' do
substitute '@version', 2, on: Config
it "returns the substituted version" do
_(subject.instance_variable_get('@version')).must_equal 2
end
end
endon is set explicitly in this case. If you omit this argument, :subject will be used as target by default which refers to the subject defined by the subject {} helper.
Alternatively, you can pass the substitution value as a block. This block will be evaluated once in the context of the test, in other words, you can use assignments done with let inside the block:
class Config
def initialize
@version = 1
end
end
describe Config do
subject do
Config.new
end
let :version do
2
end
describe 'original version' do
it "returns the original version" do
_(subject.instance_variable_get('@version')).must_equal 1
end
end
describe 'sustituted version' do
substitute '@version', on: Config do
version # set using "let" above
end
it "returns the substituted version" do
_(subject.instance_variable_get('@version')).must_equal 2
end
end
endIf both a substitution value and a substitution block are present, the latter takes precedence.
It's safe to use multiple substitute statements within one describe block.
(The spec integration is borrowed from minitest-around for elegance and compatibility.)
To install the development dependencies and then run the test suite:
bundle install
bundle exec rake # run tests once
bundle exec guard # run tests whenever files are modified
You're welcome to submit issues and contribute code by forking the project and submitting pull requests.
The gem is available as open source under the terms of the MIT License.