Skip to content

masaakiaoyagi/rspec-context_helper.rb

Repository files navigation

RSpec::ContextHelper

GitHub release (latest SemVer including pre-releases) test License: MIT Conventional Commits

This helper library is for writing tests concisely.

You can write a test as follows.

example_with("value is zero", value: 0) { expect(value).to eq 0 }

Above is the same as below.

context "value is zero" do
  let(:value) { 0 }
  it { expect(value).to eq 0 }
end

That’s basically all there is to it, but I think it will be more potent when used with a custom matcher.

Example of ActiveModel validation tests using a custom matcher
class Account
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :name, :string
  validates :name, presence: true, length: { in: 3..20 }, format: { with: /\A[0-9a-zA-Z]*\z/, message: "alphanumeric characters only" }
end

let(:account) { Account.new(name: name) }
before do
  account.valid?
end

# There is no "have_error" matcher, so you need to create one.
example_with(name: " ")      { expect(account).to have_error.on(:name).with(:blank) }
example_with(name: "a" * 2)  { expect(account).to have_error.on(:name).with(:too_short, count: 3) }
example_with(name: "a" * 3)  { expect(account).not_to have_error }
example_with(name: "a" * 20) { expect(account).not_to have_error }
example_with(name: "a" * 21) { expect(account).to have_error.on(:name).with(:too_long, count: 20) }
example_with(name: "a0a")    { expect(account).not_to have_error }
example_with(name: "a a")    { expect(account).to have_error.on(:name).with(:invalid) }
example_with(name: "a@a")    { expect(account).to have_error.on(:name).with("alphanumeric characters only") }

Installation

  1. Add the dependency to your Gemfile:

    gem "rspec-context_helper"
  2. Run bundle install

Usage

Add the following line to your spec_helper.rb:

require "rspec/context_helper"

See spec how to write a test.

API

example_with(description = nil, _meta: nil, _shared: nil, **values, &block)

Defines an exmaple with metadata, shared context and local variables.

parameters
description: context description

If description is omitted, it is automatically generated from other parameters.

_meta: metadata to be defined
examples
_meta: :foo
_meta: [:foo, :bar]
_meta: { foo: "1" }
_meta: [:foo, bar: 2]
_shared: shared context to be included
examples
_shared: :foo
_shared: [:foo, :bar]
_shared: { foo: "1" }
_shared: [:foo, bar: 2]
_shared: { foo: [:arg1, :arg2] }
_shared: { foo: { opt1: :bar } }
_shared: { foo: [:arg1, opt1: :bar] }
values: helper methods to be defined by let
You need to use a proc in order to call helper methods in the example context.
example_with(foo: bar, bar: 2) { expect(foo).to eq 2 }
# => undefined local variable or method `bar'
example_with(foo: -> { bar }, bar: 2) { expect(foo).to eq 2 }
# => OK
So you need to use a nested proc in order to define a helper method that returns a proc.
example_with(foo: -> { "proc" }) { expect(foo.call).to eq "proc" }
# => undefined method `call' for "proc":String
example_with(foo: -> { -> { "proc" } }) { expect(foo.call).to eq "proc" }
# => OK
examples
foo: "1"
foo: "1", bar: 2
foo: -> { bar }, bar: 2
foo: -> { -> { "proc" } }
examples
example_with("description") { expect(true).to eq true }

same as

context "description" do
  it { expect(true).to eq true }
end
example_with(value: 1) { expect(value).to eq 1 }

same as

context "when value is 1" do
  let(:value) { 1 }
  it { expect(value).to eq 1 }
end
example_with(_shared: "logged in") { expect(logged_in).to eq true }

same as

context "when logged in" do
  include_context "logged in"
  it { expect(logged_in).to eq true }
end
example_with(_meta: :bar) { |e| expect(e.metadata[:bar]).to eq true }

same as

context "", :bar do
  it { |e| expect(e.metadata[:bar]).to eq true }
end
example_with(_meta: { foo: 1 }) { |e| expect(e.metadata[:foo]).to eq 1 }

same as

context "", foo: 1 do
  it { |e| expect(e.metadata[:foo]).to eq 1 }
end

context_with(description = nil, _meta: nil, _shared: nil, **values, &block)

Defines an example group with metadata, shared context and local variables.

parameters

same as example_with

examples
context_with("description") { …​ }

same as

context "description" do
  ...
end
context_with(value: 1) { …​ }

same as

context "when value is 1" do
  let(:value) { 1 }
  ...
end
context_with(_shared: "logged in") { …​ }

same as

context "when logged in" do
  include_context "logged in"
  ...
end
context_with(_meta: :bar) { …​ }

same as

context "", :bar do
  ...
end
context_with(_meta: { foo: 1 }) { …​ }

same as

context "", foo: 1 do
  ...
end

Development

Run tests

$ docker compose run --rm 3.1 bundle exec rspec