Spectus

Version Yard documentation CI RuboCop License

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)

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)

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

Versioning

Spectus follows Semantic Versioning 2.0.

License

The gem is available as open source under the terms of the MIT License.


This project is sponsored by:
Sashite