Spectus
Expectation library with RFC 2119 requirement levels 🚥
Installation
Add this line to your application's Gemfile:
gem "spectus"
And then execute:
bundle
Or install it yourself as:
gem install spectus
Usage
The Spectus library is basically a module defining methods that can be used to qualify expectations in specifications.
To make Spectus available:
require "spectus"
For convenience, we will also instantiate some matchers from the Matchi library:
gem install matchi
require "matchi"
All examples here assume that this has been done.
Absolute Requirement
There is exactly one bat:
definition = Spectus.must Matchi::Be.new(1)
definition.call { "🦇".size }
# => Expresenter::Pass(actual: 1, definition: "be 1", error: nil, expected: 1, got: true, negate: false, level: :MUST)
The test is passed.
Absolute Prohibition
Truth and lies:
definition = Spectus.must_not Matchi::Be.new(true)
definition.call { false }
# => Expresenter::Pass(actual: false, definition: "be true", error: nil, expected: true, got: true, negate: true, level: :MUST)
Recommended
A well-known joke. The addition of 0.1
and 0.2
is deadly precise:
definition = Spectus.should Matchi::Be.new(0.3)
definition.call { 0.1 + 0.2 }
# => Expresenter::Pass(actual: 0.30000000000000004, definition: "be 0.3", error: nil, expected: 0.3, got: false, negate: false, level: :SHOULD)
Not Recommended
This should not be wrong:
definition = Spectus.should_not Matchi::Match.new("123456")
definition.call do
require "securerandom"
SecureRandom.hex(3)
end
# => Expresenter::Pass(actual: "bb5716", definition: "match \"123456\"", error: nil, expected: "123456", got: true, negate: true, level: :SHOULD)
In any case, as long as there are no exceptions, the test passes.
Optional
An empty array is blank, right?
definition = Spectus.may Matchi::Be.new(true)
definition.call { [].blank? }
# => Expresenter::Pass(actual: nil, definition: "be true", error: #<NoMethodError: undefined method `blank?' for []:Array>, expected: true, got: nil, negate: false, level: :MAY)
My bad! ActiveSupport was not imported. 🤦♂️
Anyways, the test passes because the exception produced is NoMethodError
, meaning that the functionality is not implemented.
Code Isolation
When executing expectations, side-effects may occur. Because they may or may not be desired, each requirement level has 2 versions:
- if it does not end with
!
, its test is performed without isolation; - if it ends with
!
, its test is performed in isolation.
Example of test without isolation:
greeting = "Hello, world!"
definition = Spectus.must Matchi::Eq.new("Hello, Alice!")
definition.call { greeting.gsub!("world", "Alice") }
# => Expresenter::Pass(actual: "Hello, Alice!", definition: "eq \"Hello, Alice!\"", error: nil, expected: "Hello, Alice!", got: true, negate: false, level: :MUST)
greeting # => "Hello, Alice!"
Example of test in isolation:
greeting = "Hello, world!"
definition = Spectus.must! Matchi::Eq.new("Hello, Alice!")
definition.call { greeting.gsub!("world", "Alice") }
# => Expresenter::Pass(actual: "Hello, Alice!", definition: "eq \"Hello, Alice!\"", error: nil, expected: "Hello, Alice!", got: true, negate: false, level: :MUST)
greeting # => "Hello, world!"
Contact
- Home page: https://github.com/fixrb/spectus
- Bugs/issues: https://github.com/fixrb/spectus/issues
- Blog post: https://batman.buzz/a-spectus-tutorial-expectations-with-rfc-2119-compliance-1fc769861c1
Versioning
Spectus follows Semantic Versioning 2.0.
License
The gem is available as open source under the terms of the MIT License.