Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
lib
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Saharspec: Specs DRY as Sahara

Gem Version Build Status

saharspec is a set of additions to RSpec. It's name is a pun on Russian word "сахар" ("sahar", means "sugar") and Sahara desert. So, it is a set of RSpec sugar, to make your specs dry as a desert.

Usage

Install it as a usual gem saharspec with gem install or gem "saharspec" in :test group of your Gemfile.

Then, probably in your spec_helper.rb

require 'saharspec'
# or feature-by-feature
require 'saharspec/its/map'
# or some part of a library
require 'saharspec/its'

Parts

Matchers

Just a random matchers I've found useful in my studies.

send_message(object, method) matcher

# before
it {
  expect(Net::HTTP).to receive(:get).with('http://google.com').and_return('not this time')
  fetcher
}

# after
require 'saharspec/matchers/send_message'

it {
  expect { fetcher }.to send_message(Net::HTTP, :get).with('http://google.com').returning('not this time')
}
# after + its_block
subject { fetcher }
its_block { is_expected.to send_message(Net::HTTP, :get).with('http://google.com').returning('not this time') }

Note: there is reasons why it is not in rspec-mocks, though, not very persuative for me.

expect { block }.to ret(value) matcher

Checks whether #call-able subject (block, method, command object), when called, return value matching to expected.

Useful when this callable subject is your primary one:

# before: option 1. subject is value
subject { 2 + x }

context 'when numeric' do
  let(:x) { 3 }
  it { is_expected.to eq 5 } # DRY
end

context 'when incompatible' do
  let(:x) { '3' }
  it { expect { subject }.to raise_error } # not DRY
end

# option 2. subject is block
subject { -> {2 + x } }

context 'when incompatible' do
  let(:x) { '3' }
  it { is_expected.to raise_error } # DRY
end

context 'when numeric' do
  let(:x) { 3 }
  it { expect(subject.call).to eq 5 } # not DRY
end

# after
require 'saharspec/matchers/ret'

subject { -> { 2 + x } }

context 'when numeric' do
  let(:x) { 3 }
  it { is_expected.to ret 5 } # DRY: notice `ret`
end

context 'when incompatible' do
  let(:x) { '3' }
  it { is_expected.to raise_error } # DRY
end

Plays really well with its_call shown below.

be_json(value) and be_json_sym(value) matchers

Simple matcher to check if string is valid JSON and optionally if it matches to expected values:

expect('{}').to be_json # ok
expect('garbage').to be_json
# expected value to be a valid JSON string but failed: 765: unexpected token at 'garbage'

expect('{"foo": "bar"}').to be_json('foo' => 'bar') # ok

# be_json_sym is more convenient to check with hash keys, parses JSON to symbols
expect('{"foo": "bar"}').to be_json_sym(foo: 'bar')

# nested matchers work, too
expect('{"foo": [1, 2, 3]').to be_json_sym(foo: array_including(3))

# We need to go deeper!
expect(something_large).to be_json_sym(include(meta: include(next_page: Integer)))

eq_multiline(text) matcher

Dedicated to checking some multiline text generators.

# before: one option

  it { expect(generated_code).to eq("def method\n  a = @b**2\n  return a + @b\nend") }

# before: another option
  it {
    expect(generated_code).to eq(%{def method
  a = @b**2
  return a + @b
end})
  }

# after
require 'saharspec/matchers/eq_multiline'

  it {
    expect(generated_code).to eq_multiline(%{
      |def method
      |  a = @b**2
      |  return a + @b
      |end
    })
  }

(empty lines before/after are removed, text deindented up to | sign)

dont: matcher negation

Allows to get rid of gazilliions of define_negated_matcher. dont is not 100% grammatically correct, yet short and readable enought. It just negates attached matcher.

# before
RSpec.define_negated_matcher :not_change, :change

it { expect { code }.to do_stuff.and not_change(obj, :attr) }

# after: no `define_negated_matcher` needed
require 'saharspec/matchers/dont'

it { expect { code }.to do_stuff.and dont.change(obj, :attr) }

its-addons

Notice: There are different opinions on usability/reasonability of its(:attribute) syntax, extracted from RSpec core and currently provided by rspec-its gem. Some find it (and a notion of description-less examples) bad practice. But if you are like me and love DRY-ness of it, probably you'll love those two ideas, taking its-syntax a bit further.

its_map

Like rspec/its, but for processing arrays:

subject { html_document.search('ul#menu > li') }

# before
it { expect(subject.map(&:text)).to all not_be_empty }

# after
require 'saharspec/its/map'

its_map(:text) { are_expected.to all not_be_empty }

its_block

Allows to DRY-ly refer to "block that calculates subject".

subject { some_operation_that_may_fail }

# before
context 'success' do
  it { is_expected.to eq 123 }
end

context 'fail' do
  it { expect { subject }.to raise_error(...) }
end

# after
require 'saharspec/its/block'

its_block { is_expected.to raise_error(...) }

its_call

Allows to DRY-ly test callable object with different arguments. Plays well with forementioned ret matcher.

Before:

# before
describe '#delete_at' do
  let(:array) { %i[a b c] }

  it { expect(array.delete_at(1) }.to eq :b }
  it { expect(array.delete_at(8) }.to eq nil }
  it { expect { array.delete_at(1) }.to change(array, :length).by(-1) }
  it { expect { array.delete_at(:b) }.to raise_error TypeError }
end

# after
require 'saharspec/its/call'

describe '#delete_at' do
  let(:array) { %i[a b c] }

  subject { array.method(:delete_at) }

  its_call(1) { is_expected.to ret :b }
  its_call(1) { is_expected.to change(array, :length).by(-1) }
  its_call(8) { is_expected.to ret nil }
  its_call(:b) { is_expected.to raise_error TypeError }
end

State & future

I use all of the components of the library on daily basis. Probably, I will extend it with other ideas and findings from time to time (next thing that needs gemification is WebMock DRY-er, allowing code like expect { code }.to request_webmock(url, params) instead of preparing stubs and then checking them). Stay tuned.

Author

Victor Shepelev

License

MIT.

About

RSpec sugar to DRY your specs

Topics

Resources

License

Packages

No packages published

Languages

You can’t perform that action at this time.