Composing specifications
A specification is a matcher: one may require another, here and in other test frameworks.
A specification says what a subject does. Some of that is said once and holds for many subjects: what an empty collection does, what a name looks like, what any plugin answers. Written as a specification of its own, it is required by the others.
In a specification: fix
The matcher fix(:Name) is met when the actual value meets the specification of that name. Here a stack must be empty when it is new, and must not be once something is pushed:
require "fix"
Fix :Empty do
on :empty? do
it MUST be_true
end
on :size do
it MUST eq 0
end
end
Fix :Stack do
it MUST fix(:Empty)
on :push, 1 do
it MUST_NOT fix(:Empty)
on :last do
it MUST eq 1
end
end
end
exit(Fix[:Stack].test { [] })
ruby stack_fix.rb stack_fix.rb:14: Success: expected [] to fix Empty stack_fix.rb:17: Success: expected [1] not to fix Empty stack_fix.rb:20: Success: expected 1 to eq 1
- The specification is found when the file is loaded, so write it before, or require the file that writes it: an unknown name raises
KeyErrorthen,no specification named :Nope. - The value is tested as a subject: each context of
Emptysends its method to it, in a child process of its own, and its lines are not written.fixis met when all of them pass, a warning or an info included. - A
fixnot met says that the specification is not met, not which of its expectations failed: test the specification on the same value, on its own, to see them. fix { … }writes a specification in place, without a name, for a requirement used once.- A specification meant for
fixis written withoutwith: the value it gets is already built, so a let cannot change it.
In Ruby: match?
A specification answers match?, as any matcher does. Its block gives the value, and the answer says whether the value meets the specification. Here Empty holds for an array, a hash and a string alike:
require "fix"
Fix :Empty do
on :empty? do
it MUST be_true
end
on :size do
it MUST eq 0
end
end
Fix[:Empty].match? { [] } # => true
Fix[:Empty].match? { {} } # => true
Fix[:Empty].match? { "" } # => true
Fix[:Empty].match? { [nil] } # => false
match? writes nothing: test is there for the lines, and run for the results.
In other frameworks: matchi-fix
The matchers of Matchi work in any test framework, and matchi-fix makes a specification of Fix one of them: Matchi::Fix.new(:Name), or Matchi::Fix.new { … } for one written in place. Install it, gem install matchi-fix, and require matchi/fix:
require "fix"
require "matchi/fix"
Fix :Empty do
on :empty? do
it MUST be_true
end
end
matcher = Matchi::Fix.new(:Empty)
matcher.to_s # => "fix Empty"
matcher.match? { [] } # => true
Spectus.must(matcher).call { [nil] }.to_s # => "Failure: expected [nil] to fix Empty"
The last line uses Spectus, which Fix is built on. In minitest, assert takes its answer: assert Matchi::Fix.new(:Empty).match? { [] }. A project that has its tests in another framework can use the same specifications in those tests, without moving them.
In a specification of Fix, it MUST Matchi::Fix.new(:Empty) and it MUST fix(:Empty) are the same: fix needs no other gem.