All Projects β†’ zverok β†’ saharspec

zverok / saharspec

Licence: MIT license
RSpec sugar to DRY your specs

Programming Languages

ruby
36898 projects - #4 most used programming language

Labels

Projects that are alternatives of or similar to saharspec

Regressor
Generate specs for your rails application the easy way. Regressor generates model and controller specs based on validations, associations, enums, database, routes, callbacks. Use regressor to capture your rails application behaviour.
Stars: ✭ 204 (+251.72%)
Mutual labels:  rspec
full-stack-web-developer
πŸ”₯ Roadmap to become a Full Stack Web Developer. What? Why? How?
Stars: ✭ 76 (+31.03%)
Mutual labels:  rspec
j8spec
Library that allows tests written in Java to follow the BDD style introduced by RSpec and Jasmine.
Stars: ✭ 45 (-22.41%)
Mutual labels:  rspec
Specjour
distributed rspec & cucumber via bonjour
Stars: ✭ 214 (+268.97%)
Mutual labels:  rspec
Apitome
Apitome: /iˈpitΙ™mΔ“/ An API documentation presentation layer for RSpec API Documentation output.
Stars: ✭ 244 (+320.69%)
Mutual labels:  rspec
opal-rspec
Opal + RSpec = β™₯️
Stars: ✭ 57 (-1.72%)
Mutual labels:  rspec
Limestone
Boilerplate Rails 6 SaaS application with Webpack, Stimulus and Docker integration.
Stars: ✭ 191 (+229.31%)
Mutual labels:  rspec
r spec-clone.rb
A minimalist RSpec clone with all the essentials.
Stars: ✭ 38 (-34.48%)
Mutual labels:  rspec
capybara-chrome
Chrome driver for Capybara using Chrome's remote debugging protocol
Stars: ✭ 27 (-53.45%)
Mutual labels:  rspec
capybara-chromedriver-logger
Enables console.log/error/info output from Javascript feature specs running with Chromedriver
Stars: ✭ 54 (-6.9%)
Mutual labels:  rspec
Rspec Nc
🚦 RSpec formatter for OS X's Notification Center
Stars: ✭ 223 (+284.48%)
Mutual labels:  rspec
Nspec
A battle hardened testing framework for C# that's heavily inspired by Mocha and RSpec.
Stars: ✭ 242 (+317.24%)
Mutual labels:  rspec
ruby-dns-mock
DNS mock server written on πŸ’Ž Ruby. Mimic any DNS records for your test environment with fake DNS server.
Stars: ✭ 50 (-13.79%)
Mutual labels:  rspec
Still life
Rails upgrade's best friend
Stars: ✭ 213 (+267.24%)
Mutual labels:  rspec
serverspec-extended-types
A set of extended types for ServerSpec 2.x
Stars: ✭ 28 (-51.72%)
Mutual labels:  rspec
Action Cable Testing
Action Cable testing utils
Stars: ✭ 192 (+231.03%)
Mutual labels:  rspec
rspec html reporter
Rspec custom formatter to produce beautiful reports from rspec
Stars: ✭ 32 (-44.83%)
Mutual labels:  rspec
rspec-tap-formatters
TAP Producer for RSpec-3
Stars: ✭ 20 (-65.52%)
Mutual labels:  rspec
solr wrapper
Wrap your tests with Solr 5+
Stars: ✭ 22 (-62.07%)
Mutual labels:  rspec
house style
A shared house style for Ruby projects
Stars: ✭ 19 (-67.24%)
Mutual labels:  rspec

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 numeric' do
  let(:x) { 3 }
  it { expect(subject.call).to eq 5 } # not DRY
end

context 'when incompatible' do
  let(:x) { '3' }
  it { is_expected.to raise_error } # 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

Linting with RuboCop RSpec

rubocop-rspec fails to properly detect RSpec constructs that Saharspec defines (its_call, its_block, its_map). Make sure to use rubocop-rspec 2.0 or newer and add the following to your .rubocop.yml:

inherit_gem:
  saharspec: config/rubocop-rspec.yml

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.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].