Minitest::Substitute
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.
Install
This gem is cryptographically signed in order to assure it hasn't been tampered with. Unless already done, please add the author's public key as a trusted certificate now:
gem cert --add <(curl -Ls https://bitcetera.com/downloads/gem-public_cert.pem)
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 --trust-policy MediumSecurity
Finally, require this gem in your test_helper.rb
or spec_helper.rb
:
require 'minitest/substitute'
Update from 0.x.x to 1.x.x
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
end
Usage
Block
To 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') # => 1
⚠️ The target on
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') # => 0
Same goes for global variables:
$verbose = false # => false
substitute '$verbose', true do
$verbose # => true
end
$verbose # => false
And 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.
Group of Tests
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
end
⚠️ The target on
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
end
If 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.)
Development
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.
License
The gem is available as open source under the terms of the MIT License.