Fix 1.0 is here
· Cyril Kato
Fix 1.0 is out. Fix is a testing framework for Ruby in which a test suite is a specification: a document that says what a subject MUST, SHOULD or MAY do, in the words of RFC 2119, and nothing about how the subject is made. I started it in Osaka in 2014 and have reworked it many times since; this is the first version I consider finished.
If it quacks like a duck
Ruby knows a duck by what it does: that is duck typing. A specification of Fix does the same: it names no class, and the test, at the end of the file, picks the subject. Here, a duck and a rubber duck, against one specification:
require "fix"
Fix :Duck do
on :quack do
it MUST eq "Quack!"
end
on :swim do
it SHOULD eq "Splash!"
end
on :fly do
it MAY be_true
end
end
class Duck
def quack = "Quack!"
def swim = "Splash!"
def fly = true
end
class RubberDuck
def quack = "Quack!"
def swim = "Splash!"
end
exit([
Fix[:Duck].test { Duck.new },
Fix[:Duck].test { RubberDuck.new }
].all?)
ruby duck_fix.rb duck_fix.rb:5: Success: expected "Quack!" to eq "Quack!" duck_fix.rb:9: Success: expected "Splash!" to eq "Splash!" duck_fix.rb:13: Success: expected true to be true duck_fix.rb:5: Success: expected "Quack!" to eq "Quack!" duck_fix.rb:9: Success: expected "Splash!" to eq "Splash!" duck_fix.rb:13: Info: NoMethodError: undefined method 'fly' for an instance of RubberDuck
The rubber duck cannot fly, but flying was only a MAY: its line is an info, and both ducks meet the specification. Three levels tell what is required, what is recommended and what is optional, and the lines say exactly what happened, one per expectation; the status of the process says whether the whole is met. The same specification can test a class of yours, a library you depend on, or the next version of either.
What 1.0 brings
- A specification is built once, when the file is loaded, and kept as data. Anything wrong in it raises before a test runs.
- Each context runs in a child process: what a subject does to a global, a constant or
ENVstays in its context, and the order of the contexts cannot change a result. - The matchers are those of Matchi, where the expected value makes the comparison, so that the object under test cannot vouch for itself.
testwrites one line per expectation and returns whether the specification is met;rungives the results as data;match?makes a specification a matcher, so that one may require another withfix(:Name).- About three hundred lines of code, with tests written by hand at full coverage, on Ruby 3.3, 3.4 and 4.0, resting on four small gems, matchi, spectus, aw and defi, each short enough to read before you trust it.
- Around it: fix-command runs the files of a project, fix-its adds the word
its, and matchi-fix brings a specification to any other test framework.
Try it
gem install fix-command
Then the guide: a first specification, the language, the matchers one by one, the command, and what changes when you come from RSpec. Every example on its pages is run before they are published. The README is the reference. Fix needs Ruby 3.3 or later and fork(2).
Write a specification for something you have, and see what it says. That is what Fix is for.