Strict assertions for Minitest.
Minitest's built-in assertions are lenient in ways that can mask bugs:
assert,assert_predicate, andassert_operatorpass for any truthy return value, not justtrue. A predicate method that accidentally returns1,"yes", or an object will silently pass.refute,refute_predicate, andrefute_operatorpass for any falsey return value, not justfalse. A method that returnsnilinstead offalsewon't be caught.assert_nilandrefute_nilcallnil?, which can be overridden on an object and mask bugs.- There's no built-in assertion that a value is exactly
trueor exactlyfalse. - There's no built-in assertion for
eql?equality (e.g. distinguishing1from1.0).
minitest-strict fixes all of this. It redefines several Minitest assertions to require strict boolean return values and adds assert_true, assert_false, and assert_eql (with corresponding refutations).
Strict assertions also make mutation testing with Mutant more effective. When assertions accept only exact boolean values, mutations like replacing true with false or swapping nil for false are reliably caught — mutations that lenient assertions would let survive.
Add to your Gemfile:
gem "minitest-strict"Then require it in your test helper:
require "minitest/autorun"
require "minitest/strict"Passes only when the value is exactly true — not merely truthy.
assert_true true # pass
assert_true 1 # fail
assert_true "yes" # fail
assert_true nil # fail
refute_true false # pass
refute_true nil # pass
refute_true 1 # pass
refute_true true # failPasses only when the value is exactly false — not merely falsey.
assert_false false # pass
assert_false nil # fail
assert_false 0 # fail
assert_false "" # fail
refute_false true # pass
refute_false nil # pass
refute_false 0 # pass
refute_false false # failNote
1 == 1.0 is true in Ruby, but 1.eql?(1.0) is false. Be mindful of this distinction when comparing numeric values.
Uses eql? instead of == for a stricter equality check. This distinguishes values that are == but not the same type.
assert_eql 1, 1 # pass
assert_eql "foo", "foo" # pass
assert_eql 1, 1.0 # fail — 1 == 1.0 is true, but 1.eql?(1.0) is false
refute_eql 1, 1.0 # pass
refute_eql 1, 2 # pass
refute_eql 1, 1 # failImportant
The following Minitest assertions are redefined to require exact boolean return values. Existing tests may fail if the methods under test return truthy/falsey values instead of true/false.
Warning
Methods like Numeric#nonzero? return self or nil instead of true or false. These will fail with minitest-strict's assert_predicate and refute_predicate.
Standard Minitest accepts any truthy/falsey return value. minitest-strict requires predicates to return exactly true or false.
assert_predicate "", :empty? # pass — String#empty? returns true
assert_predicate "hello", :empty? # fail — returns false
# Catches methods that return truthy values other than true:
assert_predicate 1, :nonzero? # fail — returns 1, not true
refute_predicate "hello", :empty? # pass — returns false
refute_predicate "", :empty? # fail — returns true
# Catches methods that return falsey values other than false:
refute_predicate 0, :nonzero? # fail — returns nil, not falseRequires operators to return exactly true or false.
assert_operator 1, :<, 2 # pass — returns true
assert_operator 2, :<, 1 # fail — returns false
refute_operator 2, :<, 1 # pass — returns false
refute_operator 1, :<, 2 # fail — returns true
# Catches operators that return non-boolean values:
obj.define_singleton_method(:<=>) { |_| 1 }
assert_operator obj, :<=>, 2 # fail — returns 1, not trueNote
The standard Minitest implementation calls nil?, which can be overridden. minitest-strict uses equal? (identity) instead, so only the actual nil object passes.
Uses equal? (identity) instead of nil?, so objects that override nil? can't fool the check.
assert_nil nil # pass
assert_nil false # fail
# Can't be tricked by overriding nil?
obj.define_singleton_method(:nil?) { true }
assert_nil obj # fail — obj is not nil
refute_nil 1 # pass
refute_nil false # pass
refute_nil nil # failThe gem is available as open source under the terms of the MIT License.