Giter VIP home page Giter VIP logo

rspec-style-guide's Introduction

RSpec Style Guide

Role models are important.

— Officer Alex J. Murphy / RoboCop
Tip
You can find a beautiful version of this guide with much improved navigation at https://rspec.rubystyle.guide.

This RSpec style guide outlines the recommended best practices for real-world programmers to write code that can be maintained by other real-world programmers.

RuboCop, a static code analyzer (linter) and formatter, has a rubocop-rspec extension, provides a way to enforce the rules outlined in this guide.

Note
This guide assumes you are using RSpec 3 or later.

You can generate a PDF copy of this guide using AsciiDoctor PDF, and an HTML copy with AsciiDoctor using the following commands:

# Generates README.pdf
asciidoctor-pdf -a allow-uri-read README.adoc

# Generates README.html
asciidoctor README.adoc
Tip

Install the rouge gem to get nice syntax highlighting in the generated document.

gem install rouge

The guide is separated into sections based on the different pieces of an entire spec file. There was an attempt to omit all obvious information, if anything is unclear, feel free to open an issue asking for further clarity.

Per the comment above, this guide is a work in progress - some rules are simply lacking thorough examples, but some things in the RSpec world change week by week or month by month. With that said, as the standard changes this guide is meant to be able to change with it.

Do not leave empty lines after feature, context or describe descriptions. It doesn’t make the code more readable and lowers the value of logical chunks.

# bad
describe Article do

  describe '#summary' do

    context 'when there is a summary' do

      it 'returns the summary' do
        # ...
      end
    end
  end
end

# good
describe Article do
  describe '#summary' do
    context 'when there is a summary' do
      it 'returns the summary' do
        # ...
      end
    end
  end
end

Leave one empty line between feature, context or describe blocks. Do not leave empty line after the last such block in a group.

# bad
describe Article do
  describe '#summary' do
    context 'when there is a summary' do
      # ...
    end
    context 'when there is no summary' do
      # ...
    end

  end
  describe '#comments' do
    # ...
  end
end

# good
describe Article do
  describe '#summary' do
    context 'when there is a summary' do
      # ...
    end

    context 'when there is no summary' do
      # ...
    end
  end

  describe '#comments' do
    # ...
  end
end

Leave one empty line after let, subject, and before/after blocks.

# bad
describe Article do
  subject { FactoryBot.create(:some_article) }
  describe '#summary' do
    # ...
  end
end

# good
describe Article do
  subject { FactoryBot.create(:some_article) }

  describe '#summary' do
    # ...
  end
end

Only group let, subject blocks and separate them from before/after blocks. It makes the code much more readable.

# bad
describe Article do
  subject { FactoryBot.create(:some_article) }
  let(:user) { FactoryBot.create(:user) }
  before do
    # ...
  end
  after do
    # ...
  end
  describe '#summary' do
    # ...
  end
end

# good
describe Article do
  subject { FactoryBot.create(:some_article) }
  let(:user) { FactoryBot.create(:user) }

  before do
    # ...
  end

  after do
    # ...
  end

  describe '#summary' do
    # ...
  end
end

Leave one empty line around it/specify blocks. This helps to separate the expectations from their conditional logic (contexts for instance).

# bad
describe '#summary' do
  let(:item) { double('something') }

  it 'returns the summary' do
    # ...
  end
  it 'does something else' do
    # ...
  end
  it 'does another thing' do
    # ...
  end
end

# good
describe '#summary' do
  let(:item) { double('something') }

  it 'returns the summary' do
    # ...
  end

  it 'does something else' do
    # ...
  end

  it 'does another thing' do
    # ...
  end
end

When subject is used, it should be the first declaration in the example group.

# bad
describe Article do
  before do
    # ...
  end

  let(:user) { FactoryBot.create(:user) }
  subject { FactoryBot.create(:some_article) }

  describe '#summary' do
    # ...
  end
end

# good
describe Article do
  subject { FactoryBot.create(:some_article) }
  let(:user) { FactoryBot.create(:user) }

  before do
    # ...
  end

  describe '#summary' do
    # ...
  end
end

When declaring subject, let!/let and before/after hooks they should be in the following order:

  • subject

  • let!/let

  • before/after

# bad
describe Article do
  before do
    # ...
  end

  after do
    # ...
  end

  let(:user) { FactoryBot.create(:user) }
  subject { FactoryBot.create(:some_article) }

  describe '#summary' do
    # ...
  end
end

# good
describe Article do
  subject { FactoryBot.create(:some_article) }
  let(:user) { FactoryBot.create(:user) }

  before do
    # ...
  end

  after do
    # ...
  end

  describe '#summary' do
    # ...
  end
end

Use contexts to make the tests clear, well organized, and easy to read.

# bad
it 'has 200 status code if logged in' do
  expect(response).to respond_with 200
end

it 'has 401 status code if not logged in' do
  expect(response).to respond_with 401
end

# good
context 'when logged in' do
  it { is_expected.to respond_with 200 }
end

context 'when logged out' do
  it { is_expected.to respond_with 401 }
end

context blocks should pretty much always have an opposite negative case. It is a code smell if there is a single context (without a matching negative case), and this code needs refactoring, or may have no purpose.

# bad - needs refactoring
describe '#attributes' do
  context 'the returned hash' do
    it 'includes the display name' do
      # ...
    end

    it 'includes the creation time' do
      # ...
    end
  end
end

# bad - the negative case needs to be tested, but isn't
describe '#attributes' do
  context 'when display name is present' do
    before do
      article.display_name = 'something'
    end

    it 'includes the display name' do
      # ...
    end
  end
end

# good
describe '#attributes' do
  subject(:attributes) { article.attributes }
  let(:article) { FactoryBot.create(:article) }

  context 'when display name is present' do
    before do
      article.display_name = 'something'
    end

    it { is_expected.to include(display_name: article.display_name) }
  end

  context 'when display name is not present' do
    before do
      article.display_name = nil
    end

    it { is_expected.not_to include(:display_name) }
  end
end

Use let and let! for data that is used across several examples in an example group. Use let! to define variables even if they are not referenced in some of the examples, e.g. when testing balancing negative cases. Do not overuse lets for primitive data, find the balance between frequency of use and complexity of the definition.

# bad
it 'finds shortest path' do
  tree = Tree.new(1 => 2, 2 => 3, 2 => 6, 3 => 4, 4 => 5, 5 => 6)
  expect(dijkstra.shortest_path(tree, from: 1, to: 6)).to eq([1, 2, 6])
end

it 'finds longest path' do
  tree = Tree.new(1 => 2, 2 => 3, 2 => 6, 3 => 4, 4 => 5, 5 => 6)
  expect(dijkstra.longest_path(tree, from: 1, to: 6)).to eq([1, 2, 3, 4, 5, 6])
end

# good
let(:tree) { Tree.new(1 => 2, 2 => 3, 2 => 6, 3 => 4, 4 => 5, 5 => 6) }

it 'finds shortest path' do
  expect(dijkstra.shortest_path(tree, from: 1, to: 6)).to eq([1, 2, 6])
end

it 'finds longest path' do
  expect(dijkstra.longest_path(tree, from: 1, to: 6)).to eq([1, 2, 3, 4, 5, 6])
end

Use let definitions instead of instance variables.

# bad
before { @name = 'John Wayne' }

it 'reverses a name' do
  expect(reverser.reverse(@name)).to eq('enyaW nhoJ')
end

# good
let(:name) { 'John Wayne' }

it 'reverses a name' do
  expect(reverser.reverse(name)).to eq('enyaW nhoJ')
end

Use shared examples to reduce code duplication.

# bad
describe 'GET /articles' do
  let(:article) { FactoryBot.create(:article, owner: owner) }

  before { page.driver.get '/articles' }

  context 'when user is the owner' do
    let(:user) { owner }

    it 'shows all owned articles' do
      expect(page.status_code).to be(200)
      contains_resource resource
    end
  end

  context 'when user is an admin' do
    let(:user) { FactoryBot.create(:user, :admin) }

    it 'shows all resources' do
      expect(page.status_code).to be(200)
      contains_resource resource
    end
  end
end

# good
describe 'GET /articles' do
  let(:article) { FactoryBot.create(:article, owner: owner) }

  before { page.driver.get '/articles' }

  shared_examples 'shows articles' do
    it 'shows all related articles' do
      expect(page.status_code).to be(200)
      contains_resource resource
    end
  end

  context 'when user is the owner' do
    let(:user) { owner }

    include_examples 'shows articles'
  end

  context 'when user is an admin' do
    let(:user) { FactoryBot.create(:user, :admin) }

    include_examples 'shows articles'
  end
end

# good
describe 'GET /devices' do
  let(:resource) { FactoryBot.create(:device, created_from: user) }

  it_behaves_like 'a listable resource'
  it_behaves_like 'a paginable resource'
  it_behaves_like 'a searchable resource'
  it_behaves_like 'a filterable list'
end

Don’t specify :each/:example scope for before/after/around blocks, as it is the default. Prefer :example when explicitly indicating the scope.

# bad
describe '#summary' do
  before(:example) do
    # ...
  end

  # ...
end

# good
describe '#summary' do
  before do
    # ...
  end

  # ...
end

Use :context instead of the ambiguous :all scope in before/after hooks.

# bad
describe '#summary' do
  before(:all) do
    # ...
  end

  # ...
end

# good
describe '#summary' do
  before(:context) do
    # ...
  end

  # ...
end

Avoid using before/after with :context scope. Beware of the state leakage between the examples.

For examples two styles are considered acceptable. The first variant is separate example for each expectation, which comes with a cost of repeated context initialization. The second variant is multiple expectations per example with aggregate_failures tag set for a group or example. Use your best judgement in each case, and apply your strategy consistently.

# good - one expectation per example
describe ArticlesController do
  #...

  describe 'GET new' do
    it 'assigns a new article' do
      get :new
      expect(assigns[:article]).to be_a(Article)
    end

    it 'renders the new article template' do
      get :new
      expect(response).to render_template :new
    end
  end
end

# good - multiple expectations with aggregated failures
describe ArticlesController do
  #...

  describe 'GET new', :aggregate_failures do
    it 'assigns new article and renders the new article template' do
      get :new
      expect(assigns[:article]).to be_a(Article)
      expect(response).to render_template :new
    end
  end

  # ...
end

When several tests relate to the same subject, use subject to reduce repetition.

# bad
it { expect(hero.equipment).to be_heavy }
it { expect(hero.equipment).to include 'sword' }

# good
subject(:equipment) { hero.equipment }

it { expect(equipment).to be_heavy }
it { expect(equipment).to include 'sword' }

Use named subject when possible. Only use anonymous subject declaration when you don’t reference it in any tests, e.g. when is_expected is used.

# bad
describe Article do
  subject { FactoryBot.create(:article) }

  it 'is not published on creation' do
    expect(subject).not_to be_published
  end
end

# good
describe Article do
  subject { FactoryBot.create(:article) }

  it 'is not published on creation' do
    is_expected.not_to be_published
  end
end

# even better
describe Article do
  subject(:article) { FactoryBot.create(:article) }

  it 'is not published on creation' do
    expect(article).not_to be_published
  end
end

When you reassign subject with different attributes in different contexts, give different names to the subject, so it’s easier to see what the actual subject represents.

# bad
describe Article do
  context 'when there is an author' do
    subject(:article) { FactoryBot.create(:article, author: user) }

    it 'shows other articles by the same author' do
      expect(article.related_stories).to include(story1, story2)
    end
  end

  context 'when the author is anonymous' do
    subject(:article) { FactoryBot.create(:article, author: nil) }

    it 'matches stories by title' do
      expect(article.related_stories).to include(story3, story4)
    end
  end
end

# good
describe Article do
  context 'when article has an author' do
    subject(:article) { FactoryBot.create(:article, author: user) }

    it 'shows other articles by the same author' do
      expect(article.related_stories).to include(story1, story2)
    end
  end

  context 'when the author is anonymous' do
    subject(:guest_article) { FactoryBot.create(:article, author: nil) }

    it 'matches stories by title' do
      expect(guest_article.related_stories).to include(story3, story4)
    end
  end
end

Don’t stub methods of the object under test, it’s a code smell and often indicates a bad design of the object itself.

# bad
describe 'Article' do
  subject(:article) { Article.new }

  it 'indicates that the author is unknown' do
    allow(article).to receive(:author).and_return(nil)
    expect(article.description).to include('by an unknown author')
  end
end

# good - with correct subject initialization
describe 'Article' do
  subject(:article) { Article.new(author: nil) }

  it 'indicates that the author is unknown' do
    expect(article.description).to include('by an unknown author')
  end
end

# good - with better object design
describe 'Article' do
  subject(:presenter) { ArticlePresenter.new(article) }
  let(:article) { Article.new }

  it 'indicates that the author is unknown' do
    allow(article).to receive(:author).and_return(nil)
    expect(presenter.description).to include('by an unknown author')
  end
end

Use specify if the example doesn’t have a description, use it for examples with descriptions. An exception is one-line example, where it is preferable. specify is also useful when the docstring does not read well off of it.

# bad
it do
  # ...
end

specify 'it sends an email' do
  # ...
end

specify { is_expected.to be_truthy }

it '#do_something is deprecated' do
  ...
end

# good
specify do
  # ...
end

it 'sends an email' do
  # ...
end

it { is_expected.to be_truthy }

specify '#do_something is deprecated' do
  ...
end

Do not write iterators to generate tests. When another developer adds a feature to one of the items in the iteration, they must then break it out into a separate test - they are forced to edit code that has nothing to do with their pull request.

# bad
[:new, :show, :index].each do |action|
  it 'returns 200' do
    get action
    expect(response).to be_ok
  end
end

# good - more verbose, but better for the future development
describe 'GET new' do
  it 'returns 200' do
    get :new
    expect(response).to be_ok
  end
end

describe 'GET show' do
  it 'returns 200' do
    get :show
    expect(response).to be_ok
  end
end

describe 'GET index' do
  it 'returns 200' do
    get :index
    expect(response).to be_ok
  end
end

Avoid incidental state as much as possible.

# bad
it 'publishes the article' do
  article.publish

  # Creating another shared Article test object above would cause this
  # test to break
  expect(Article.count).to eq(2)
end

# good
it 'publishes the article' do
  expect { article.publish }.to change(Article, :count).by(1)
end

Be careful not to focus on being 'DRY' by moving repeated expectations into a shared environment too early, as this can lead to brittle tests that rely too much on one another.

In general, it is best to start with doing everything directly in your it blocks even if it is duplication and then refactor your tests after you have them working to be a little more DRY. However, keep in mind that duplication in test suites is NOT frowned upon, in fact it is preferred if it provides easier understanding and reading of a test.

Use Factory Bot to create test data in integration tests. You should very rarely have to use ModelName.create within an integration spec. Do not use fixtures as they are not nearly as maintainable as factories.

# bad
subject(:article) do
  Article.create(
    title: 'Piccolina',
    author: 'John Archer',
    published_at: '17 August 2172',
    approved: true
  )
end

# good
subject(:article) { FactoryBot.create(:article) }
Note
When talking about unit tests the best practice would be to use neither fixtures nor factories. Put as much of your domain logic in libraries that can be tested without needing complex, time consuming setup with either factories or fixtures.

Do not load more data than needed to test your code.

# good
RSpec.describe User do
  describe ".top" do
    subject { described_class.top(2) }

    before { FactoryBot.create_list(:user, 3) }

    it { is_expected.to have(2).items }
  end
end

Prefer using verifying doubles over normal doubles.

Verifying doubles are a stricter alternative to normal doubles that provide guarantees, e.g. a failure will be triggered if an invalid method is being stubbed or a method is called with an invalid number of arguments.

In general, use doubles with more isolated/behavioral tests rather than with integration tests.

Note
There is no justification for turning verify_partial_doubles configuration option off. That will significantly reduce the confidence in partial doubles.
# good - verifying instance double
article = instance_double('Article')
allow(article).to receive(:author).and_return(nil)

presenter = described_class.new(article)
expect(presenter.title).to include('by an unknown author')


# good - verifying object double
article = object_double(Article.new, valid?: true)
expect(article.save).to be true


# good - verifying partial double
allow(Article).to receive(:find).with(5).and_return(article)


# good - verifying class double
notifier = class_double('Notifier')
expect(notifier).to receive(:notify).with('suspended as')
Note
If you stub a method that could give a false-positive test result, you have gone too far.

Always use Timecop instead of stubbing anything on Time or Date.

describe InvoiceReminder do
  subject(:time_with_offset) { described_class.new.get_offset_time }

  # bad
  it 'offsets the time 2 days into the future' do
    current_time = Time.now
    allow(Time).to receive(:now).and_return(current_time)
    expect(time_with_offset).to eq(current_time + 2.days)
  end

  # good
  it 'offsets the time 2 days into the future' do
    Timecop.freeze(Time.now) do
      expect(time_with_offset).to eq 2.days.from_now
    end
  end
end

Stub HTTP requests when the code is making them. Avoid hitting real external services.

Use webmock and VCR separately or together.

# good
context 'with unauthorized access' do
  let(:uri) { 'http://api.lelylan.com/types' }

  before { stub_request(:get, uri).to_return(status: 401, body: fixture('401.json')) }

  it 'returns access denied' do
    page.driver.get uri
    expect(page).to have_content 'Access denied'
  end
end

Do not explicitly declare classes, modules, or constants in example groups. Stub constants instead.

Note
Constants, including classes and modules, when declared in a block scope, are defined in global namespace, and leak between examples.
# bad
describe SomeClass do
  CONSTANT_HERE = 'I leak into global namespace'
end

# good
describe SomeClass do
  before do
    stub_const('CONSTANT_HERE', 'I only exist during this example')
  end
end

# bad
describe SomeClass do
  class FooClass < described_class
    def double_that
      some_base_method * 2
    end
  end

  it { expect(FooClass.new.double_that).to eq(4) }
end

# good - anonymous class, no constant needs to be defined
describe SomeClass do
  let(:foo_class) do
    Class.new(described_class) do
      def double_that
        some_base_method * 2
      end
    end
  end

  it { expect(foo_class.new.double_that).to eq(4) }
end

# good - constant is stubbed
describe SomeClass do
  before do
    foo_class = Class.new(described_class) do
                  def do_something
                  end
                end
    stub_const('FooClass', foo_class)
  end

  it { expect(FooClass.new.double_that).to eq(4) }
end

Avoid using implicit block expectations.

# bad
subject { -> { do_something } }
it { is_expected.to change(something).to(new_value) }

# good
it 'changes something to a new value' do
  expect { do_something }.to change(something).to(new_value)
end

Context descriptions should describe the conditions shared by all the examples within. Full example names (formed by concatenation of all nested block descriptions) should form a readable sentence.

A typical description will be an adjunct phrase starting with 'when', 'with', 'without', or similar words.

# bad - 'Summary user logged in no display name shows a placeholder'
describe 'Summary' do
 context 'user logged in' do
   context 'no display name' do
     it 'shows a placeholder' do
     end
   end
 end
end

# good - 'Summary when the user is logged in when the display name is blank shows a placeholder'
describe 'Summary' do
 context 'when the user is logged in' do
   context 'when the display name is blank' do
     it 'shows a placeholder' do
     end
   end
 end
end

it/specify block descriptions should never end with a conditional. This is a code smell that the it most likely needs to be wrapped in a context.

# bad
it 'returns the display name if it is present' do
  # ...
end

# good
context 'when display name is present' do
  it 'returns the display name' do
    # ...
  end
end

# This encourages the addition of negative test cases that might have
# been overlooked
context 'when display name is not present' do
  it 'returns nil' do
    # ...
  end
end

Keep example description shorter than 60 characters.

Write the example that documents itself, and generates proper documentation format output.

# bad
it 'rewrites "should not return something" as "does not return something"' do
  # ...
end

# good
it 'rewrites "should not return something"' do
  expect(rewrite('should not return something')).to
    eq 'does not return something'
end

# good - self-documenting
specify do
  expect(rewrite('should not return something')).to
    eq 'does not return something'
end

Do not write 'should' or 'should not' in the beginning of your example docstrings. The descriptions represent actual functionality, not what might be happening. Use the third person in the present tense.

# bad
it 'should return the summary' do
  # ...
end

# good
it 'returns the summary' do
  # ...
end

Be clear about what method you are describing. Use the Ruby documentation convention of . when referring to a class method’s name and # when referring to an instance method’s name.

# bad
describe 'the authenticate method for User' do
  # ...
end

describe 'if the user is an admin' do
  # ...
end

# good
describe '.authenticate' do
  # ...
end

describe '#admin?' do
  # ...
end

Always use the newer expect syntax.

Configure RSpec to only accept the new expect syntax.

# bad
it 'creates a resource' do
  response.should respond_with_content_type(:json)
end

# good
it 'creates a resource' do
  expect(response).to respond_with_content_type(:json)
end

Use RSpec’s predicate matcher methods when possible.

describe Article do
  subject(:article) { FactoryBot.create(:article) }

  # bad
  it 'is published' do
    expect(article.published?).to be true
  end

  # good
  it 'is published' do
    expect(article).to be_published
  end

  # even better
  it { is_expected.to be_published }
end

Use built-in matchers.

# bad
it 'includes a title' do
  expect(article.title.include?('a lengthy title')).to be true
end

# good
it 'includes a title' do
  expect(article.title).to include 'a lengthy title'
end

Avoid using be matcher without arguments. It is too generic, as it pass on everything that is not nil or false. If that is the exact intent, use be_truthy. In all other cases it’s better to specify what exactly is the expected value.

# bad
it 'has author' do
  expect(article.author).to be
end

# good
it 'has author' do
  expect(article.author).to be_truthy # same as the original
  expect(article.author).not_to be_nil # `be` is often used to check for non-nil value
  expect(article.author).to be_an(Author) # explicit check for the type of the value
end

Extract frequently used common logic from your examples into custom matchers.

# bad
it 'returns JSON with temperature in Celsius' do
  json = JSON.parse(response.body).with_indifferent_access
  expect(json[:celsius]).to eq 30
end

it 'returns JSON with temperature in Fahrenheit' do
  json = JSON.parse(response.body).with_indifferent_access
  expect(json[:fahrenheit]).to eq 86
end

# good
it 'returns JSON with temperature in Celsius' do
  expect(response).to include_json(celsius: 30)
end

it 'returns JSON with temperature in Fahrenheit' do
  expect(response).to include_json(fahrenheit: 86)
end

Avoid using allow_any_instance_of/expect_any_instance_of. It might be an indication that the object under test is too complex, and is ambiguous when used with receive counts.

# bad
it 'has a name' do
  allow_any_instance_of(User).to receive(:name).and_return('Tweedledee')
  expect(account.name).to eq 'Tweedledee'
end

# good
let(:account) { Account.new(user) }

it 'has a name' do
  allow(user).to receive(:name).and_return('Tweedledee')
  expect(account.name).to eq 'Tweedledee'
end

Use third-party matcher libraries that provide convenience helpers that will significantly simplify the examples, Shoulda Matchers are one worth mentioning.

# bad
describe '#title' do
  it 'is required' do
    article.title = nil
    article.valid?
    expect(article.errors[:title])
      .to contain_exactly('Article has no title')
    not
  end
end

# good
describe '#title' do
  it 'is required' do
    expect(article).to validate_presence_of(:title)
      .with_message('Article has no title')
  end
end

Test what you see. Deeply test your models and your application behaviour (integration tests). Do not add useless complexity testing controllers.

This is an open debate in the Ruby community and both sides have good arguments supporting their idea. People supporting the need of testing controllers will tell you that your integration tests don’t cover all use cases and that they are slow. Both are wrong. It is possible to cover all use cases and it’s possible to make them fast.

The directory structure of the view specs spec/views matches the one in app/views. For example the specs for the views in app/views/users are placed in spec/views/users.

The naming convention for the view specs is adding _spec.rb to the view name, for example the view _form.html.erb has a corresponding spec _form.html.erb_spec.rb.

The outer describe block uses the path to the view without the app/views part. This is used by the render method when it is called without arguments.

# spec/views/articles/new.html.erb_spec.rb
describe 'articles/new.html.erb' do
  # ...
end

Always mock the models in the view specs. The purpose of the view is only to display information.

The method assign supplies the instance variables which the view uses and are supplied by the controller.

# spec/views/articles/edit.html.erb_spec.rb
describe 'articles/edit.html.erb' do
  it 'renders the form for a new article creation' do
    assign(:article, double(Article).as_null_object)
    render
    expect(rendered).to have_selector('form',
      method: 'post',
      action: articles_path
    ) do |form|
      expect(form).to have_selector('input', type: 'submit')
    end
  end
end

Prefer capybara negative selectors over to_not with positive ones.

# bad
expect(page).to_not have_selector('input', type: 'submit')
expect(page).to_not have_xpath('tr')

# good
expect(page).to have_no_selector('input', type: 'submit')
expect(page).to have_no_xpath('tr')

When a view uses helper methods, these methods need to be stubbed. Stubbing the helper methods is done on the template object:

# app/helpers/articles_helper.rb
class ArticlesHelper
  def formatted_date(date)
    # ...
  end
end
# app/views/articles/show.html.erb
<%= 'Published at: #{formatted_date(@article.published_at)}' %>
# spec/views/articles/show.html.erb_spec.rb
describe 'articles/show.html.erb' do
  it 'displays the formatted date of article publishing' do
    article = double(Article, published_at: Date.new(2012, 01, 01))
    assign(:article, article)

    allow(template).to_receive(:formatted_date).with(article.published_at).and_return('01.01.2012')

    render
    expect(rendered).to have_content('Published at: 01.01.2012')
  end
end

The helpers specs are separated from the view specs in the spec/helpers directory.

Mock the models and stub their methods. Testing the controller should not depend on the model creation.

Test only the behaviour the controller should be responsible about:

  • Execution of particular methods

  • Data returned from the action - assigns, etc.

  • Result from the action - template render, redirect, etc.

# Example of a commonly used controller spec
# spec/controllers/articles_controller_spec.rb
# We are interested only in the actions the controller should perform
# So we are mocking the model creation and stubbing its methods
# And we concentrate only on the things the controller should do

describe ArticlesController do
  # The model will be used in the specs for all methods of the controller
  let(:article) { double(Article) }

  describe 'POST create' do
    before { allow(Article).to receive(:new).and_return(article) }

    it 'creates a new article with the given attributes' do
      expect(Article).to receive(:new).with(title: 'The New Article Title').and_return(article)
      post :create, message: { title: 'The New Article Title' }
    end

    it 'saves the article' do
      expect(article).to receive(:save)
      post :create
    end

    it 'redirects to the Articles index' do
      allow(article).to receive(:save)
      post :create
      expect(response).to redirect_to(action: 'index')
    end
  end
end

Use context when the controller action has different behaviour depending on the received params.

# A classic example for use of contexts in a controller spec is creation or update when the object saves successfully or not.

describe ArticlesController do
  let(:article) { double(Article) }

  describe 'POST create' do
    before { allow(Article).to receive(:new).and_return(article) }

    it 'creates a new article with the given attributes' do
      expect(Article).to receive(:new).with(title: 'The New Article Title').and_return(article)
      post :create, article: { title: 'The New Article Title' }
    end

    it 'saves the article' do
      expect(article).to receive(:save)
      post :create
    end

    context 'when the article saves successfully' do
      before do
        allow(article).to receive(:save).and_return(true)
      end

      it 'sets a flash[:notice] message' do
        post :create
        expect(flash[:notice]).to eq('The article was saved successfully.')
      end

      it 'redirects to the Articles index' do
        post :create
        expect(response).to redirect_to(action: 'index')
      end
    end

    context 'when the article fails to save' do
      before do
        allow(article).to receive(:save).and_return(false)
      end

      it 'assigns @article' do
        post :create
        expect(assigns[:article]).to eq(article)
      end

      it "re-renders the 'new' template" do
        post :create
        expect(response).to render_template('new')
      end
    end
  end
end

Do not mock the models in their own specs.

Use FactoryBot.create to make real objects, or just use a new (unsaved) instance with subject.

describe Article do
  subject(:article) { FactoryBot.create(:article) }

  it { is_expected.to be_an Article }
  it { is_expected.to be_persisted }
end

It is acceptable to mock other models or child objects.

Create the model for all examples in the spec to avoid duplication.

describe Article do
  let(:article) { FactoryBot.create(:article) }
end

Add an example ensuring that the model created with FactoryBot.create is valid.

describe Article do
  it 'is valid with valid attributes' do
    expect(article).to be_valid
  end
end

When testing validations, use expect(model.errors[:attribute].size).to eq(x) to specify the attribute which should be validated. Using be_valid does not guarantee that the problem is in the intended attribute.

# bad
describe '#title' do
  it 'is required' do
    article.title = nil
    expect(article).to_not be_valid
  end
end

# preferred
describe '#title' do
  it 'is required' do
    article.title = nil
    article.valid?
    expect(article.errors[:title].size).to eq(1)
  end
end

Add a separate describe for each attribute which has validations.

describe '#title' do
  it 'is required' do
    article.title = nil
    article.valid?
    expect(article.errors[:title].size).to eq(1)
  end
end

describe '#name' do
  it 'is required' do
    article.name = nil
    article.valid?
    expect(article.errors[:name].size).to eq(1)
  end
end

When testing uniqueness of a model attribute, name the other object another_object.

describe Article do
  describe '#title' do
    it 'is unique' do
      another_article = FactoryBot.create(:article, title: article.title)
      article.valid?
      expect(article.errors[:title].size).to eq(1)
    end
  end
end

The model in the mailer spec should be mocked. The mailer should not depend on the model creation.

The mailer spec should verify that:

  • the subject is correct

  • the sender e-mail is correct

  • the e-mail is sent to the correct recipient

  • the e-mail contains the required information

describe SubscriberMailer do
  let(:subscriber) { double(Subscription, email: '[email protected]', name: 'John Doe') }

  describe 'successful registration email' do
    subject(:email) { SubscriptionMailer.successful_registration_email(subscriber) }

    it { is_expected.to have_attributes(subject: 'Successful Registration!', from: ['infor@your_site.com'], to: [subscriber.email]) }

    it 'contains the subscriber name' do
      expect(email.body.encoded).to match(subscriber.name)
    end
  end
end

Correctly set up RSpec configuration globally (~/.rspec), per project (.rspec), and in project override file that is supposed to be kept out of version control (.rspec-local). Use rspec --init to generate .rspec and spec/spec_helper.rb files.

# .rspec
--color
--require spec_helper

# .rspec-local
--profile 2

Nothing written in this guide is set in stone. Everyone is welcome to contribute, so that we could ultimately create a resource that will be beneficial to the entire Ruby community.

Feel free to open tickets or send pull requests with improvements. Thanks in advance for your help!

You can also support the project (and RuboCop) with financial contributions via Patreon.

It’s easy, just follow the contribution guidelines below:

Inspiration was taken from the following:

This guide was maintained by ReachLocal for a long while.

This guide includes material originally present in BetterSpecs (newer site older site), sponsored by Lelylan and maintained by Andrea Reginato and many others for a long while.

rspec-style-guide's People

Contributors

3limin4t0r avatar andreareginato avatar andyw8 avatar batshoes avatar bbatsov avatar boardfish avatar brettgoss avatar darhazer avatar dependabot[bot] avatar drewdeponte avatar drujensen avatar edmorley avatar elebow avatar gbpereira avatar goronfreeman avatar hungnh103 avatar justinleveck avatar koic avatar ma2gedev avatar matthew-puku avatar nicholaides avatar noanonoa avatar pcarn avatar pirj avatar russcloak avatar ryanhedges avatar schinery avatar siegfault avatar slabounty avatar ydakuka avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rspec-style-guide's Issues

Restore references to original discussions and sources

After conversion to AsciiDoc (#95) due to significant markup changes, the references to original discussions and sources were lost.
A good place to check them is the result of git blame.

Example:

 Pick "use factories" rule from betterspecs

Original links:
http://www.betterspecs.org/#data
http://lelylan.github.io/betterspecs/#data
Discussion: lelylan/betterspecs#11
https://github.com/lelylan/betterspecs/blob/gh-pages/index.html#L573

Related:
http://blog.steveklabnik.com/posts/2012-07-14-why-i-don-t-like-factory_girl

Since this is a living document, it's very hard to rely on git blame in the long run to keep track of the sources.
Having reference links to discussions in some way would be very helpful for the curious about the reasoning behind the recommendations.

Related to #89

Recommend using `pry-resque/rspec`

It's very useful to stop at breakpoint where the test is about to break with possibilities to make changes to code/context and re-run the examples on the fly, without leaving the console.

Names subjects

Rubocop-RSpec forces the usage of named subject, when the subject is referenced as in the example below:

describe Article do
  subject { FactoryGirl.create(:article) }

  it 'is not published on creation' do
    expect(subject).not_to be_published
  end
end

And in fact it becomes even more readable:

describe Article do
  subject(:article) { FactoryGirl.create(:article) }

  it 'is not published on creation' do
    expect(article).not_to be_published
  end
end

So we might want to reword the "Use subject when possible" rule

Implicit block expectation syntax

The following syntax is possible

subject { -> { do_something } }
it { is_expected.to change(:something).to(...) }

which is the same as:

it { expect { do_something }.to change(:something).to(...) }

but the former comes with a few drawbacks:

  • it's an exception to the case when subject is not cached
  • it's near impossible to detect with static analysis tools (RuboCop, Reek, ...) that it is actually a block expectation
  • it's rarely used and not known by many

Both examples below will pass:

subject { -> { fail 'Something bad happened' } }
specify do
  is_expected.to raise_error(/Something/)
  is_expected.to raise_error(/bad happened/)
end
before { @a=1 }
subject { -> {@a+=1} }
specify do
  is_expected.to change { @a }.by(1)
  is_expected.to change { @a }.by(1)
  is_expected.to change { @a }.by(1)
  is_expected.to change { @a }.by(1)
  is_expected.to change { @a }.by(1)
  is_expected.to change { @a }.by(1)
end

Keeping in mind that:

  • we don't recommend using an implicit subject (should/is_expected) except in some cases,
  • the benefit of using this syntax is quite low

There seem to be not much usage cases left for implicit block expectation syntax.
What do you think of recommending against this syntax?

Related blog post on blog.rubystyle.guide, reddit comments.

"context descriptions" when there are independent clauses

context block descriptions should always start with 'when', and be in the form of a sentence with proper grammar.

What if we have independent clauses. Then would it be permissible to join with an and when?

e.g.:

context 'when condition A is true' do
  context 'and when condition B is false' do
    ...
  end
  context 'and when condition B is true' do
    ...
  end
end

Then the output from --format documentation reads like a proper sentence. Otherwise, if you force each block to start with "when", it doesn't make readable sentences.

Convert to AsciiDoc

In light of #87 I think it'd be nice if we adopted a better format for the guide, namely AsciiDoc. It would allow us to have native admonitions, footnotes, sane nesting of list items, etc and to easily export the guide in various formats. That would also make it trivial to publish a simple site from the guide down the road if we want to.

It's trivial to convert to AsciiDoc with this tool https://github.com/asciidoctor/kramdown-asciidoc (created by the maintainer of AsciiDoctor himself).

In general my longer term plan is to convert all guides to AsciiDoc and maybe even RuboCop's own documentation.

Explain how metadata work

Metadata is very useful and is able to include shared contexts, include group/example-specific helpers and hooks.

Recommend using `shared_context`

  1. shared_context is undeservedly omitted from the style guide, and it's sometimes extremely useful.

  2. It makes sense to add another rule at the same time to describe the difference between shared_example and shared_context, so they are not used interchangeably, the latter should not contain examples/groups.

Relax context wording rule (allow with and without)

Currently the style guide says that context should always start with when, but with and without are also good descriptions of context and they are allowed by default in rubocop-rspec.

So I suggest to add them to the style guide as well.

rubocop-rspec pull request
betterspecs pull request

Rationale:

# not so hot
context 'with sprinkles' ...
context 'when there are no sprinkles on top' ...

# better
context 'with sprinkles' ...
context 'without sprinkles' ...

We can not use expect in a describe block, can we?

Noticed here as a good example:

# Refactored
describe '#attributes' do
  subject { FactoryGirl.create(:article) }

  expect(subject.attributes).to include subject.display_name
  expect(subject.attributes).to include subject.created_at
end

Difference between subject and let

I've spent the last half hour trying to google the difference between subject and let, after having already read the RSpec docs, which is how I came across this guide. Which I'm thankfully for, because there's lots of useful suggestions here.

However, when I finally got to the subject/lets section, my excitement turned to disappointment. I still don't understand the difference between subject and let...

Help?

Relax factories vs fixtures rule

Fixtures as something created before suite might be useful, specifically e.g. for a list of countries that are later used by other records (via say, user.country_id especially when there's a foreign key constraint).
There should be a balance between creating all the records, as this may negatively impact the performance when only a few examples are run vs save big when multiple examples are run.

Unfortunately, in Rails, the Rails fixtures are being loaded before each example, and that neglects the benefits, even despite the recently introduced bulk loading.

Advise using `verify_doubled_constant_names`

In addition to verify_partial_doubles the above option may come handy to verify that you are stubbing an existing constant.

      # When this is set to true, an error will be raised when
      # `instance_double` or `class_double` is given the name of an undefined
      # constant. You probably only want to set this when running your entire
      # test suite, with all production code loaded. Setting this for an
      # isolated unit test will prevent you from being able to isolate it!

Example

With this option set to false the following example will, surprisingly, pass:

describe UserGreeter do
  it 'picks user name and prints greeting' do
    user = instance_double('Usor') # notice a typo
    allow(user).to receive(:name).and_return('John')
    expect { subject.process(user) }.to output('Hello, John')
end

When User renames its name method to full_name, this spec will still pass, but UserGreeter will blow up in production with NoMethodError.

Real-life Examples

Example violations (resulting with errors when the option is set to true):

# Typo: notice the leading space
let(:api) { instance_double(' ThirdParty::API') }

# `instance_double` expects a class name
let(:charge) { instance_double('payment', id: 1) } # "payment" is not a defined constant. Perhaps you misspelt it?

# Does not exist outside of the test code
let(:action) { instance_double('Action') } # "Action" is not a defined constant (though "Namespace::Action" is)

# Not loaded
let(:settings) { class_double('Settings') } # "Settings" is not a defined constant

# Double stubbing
before do
  stub_const('ActionClass', Class.new)
end
let(:action) { instance_double(ActionClass) } "ActionClass" is not a defined constant

Recommend preferring helper methods to hooks

Don't overuse hooks, prefer explicitness.
Helper methods are more explicit and even provide more flexibility

An example off the top of my head:

# bad
let(:article) { Article.new }

before do
  article.author = author
end

context 'with an author' do
  let(:author) { Author.new('John') }

  it "returns article's author name" do
    expect(article.author_name).to eq 'John'
  end
end

context 'without an author' do
  let(:author) { nil }

  it "returns a placeholder" do
    expect(article.author_name).to eq 'Unknown'
  end
end

# good
def article_for_author(author)
  article = Article.new
  article.author = author
  article
end

context 'with an author' do
  let(:author) { Author.new('John') }

  it "returns article's author name" do
    article = article_for_author(author)
    expect(article.author_name).to eq 'John'
  end
end

context 'without an author' do
  it "returns a placeholder" do
    article = article_for_author(nil)
    expect(article.author_name).to eq 'Unknown'
  end
end

Just like with #56, this should be used when there's a compelling benefit for extraction - improved readability or code reuse.

How to cite sources?

For example:

Use :context instead of the ambiguous :all scope in before/after hooks.

There's a section in Effective Testing With RSpec 3 which provides similar advice:

https://books.google.ca/books?id=8g5QDwAAQBAJ&pg=PT195&lpg=PT195

I think it would be useful to cite this as an 'authoritative' reference (since the book is written by an RSpec maintainer), but I'm not sure what's the most appropriate way. Should it link to Google Books? Or Safari Books Online (which requires paid access)? Or should it just mention the book by its title?

Confusing `described_class`

Hey, everyone!

described_class is no doubt widely used in specs, there are 1376 usages in repositories listed in real-world-ruby-apps.

According to described_class docs:

If the first argument to [...] example group is a class, the class is exposed to each example via the described_class() method.

However, in fact it's slightly different:

It's more like described_thing -- which is a class if you pass a class or whatever object you pass.

-- Myron Marston

With one exception though, when a string is passed as the first argument.

The reason for the string exception is that strings, when passed to describe or context, are taken to be an English description of the context, and not the object-being-described.
Any other type of object is assumed to be the thing the user is describing.

-- Myron Marston

One common mistake is passing metadata without a proper docstring, e.g.:

RSpec.describe SomeClass do
  describe controller: true do
    it { expect(described_class).to eq(SomeClass) }
  end
end

results in:

  1) SomeClass {:controller=>true} should eq SomeClass
     Failure/Error: it { expect(described_class).to eq(SomeClass) }

       expected: SomeClass
            got: {:controller=>true}

It may be unclear why the first argument is taken from the innermost example group, not outermost (SomeClass).
There was a breaking change introduced in RSpec 3.0 to take the first argument of the innermost example group as described_class, not outermost.
Unfortunately, this was not reflected in the docs (1, 2), and it's still not (it's on master but not on 3-8-maintenance), even after this pull request.

I also argue against using classes there (with describe) preferring strings

-- Jon Rowe

# bad
RSpec.describe Article do

# good
RSpec.describe “Article” do

I recommend sticking to describe <string arg> for nested example groups as it avoids confusion.

-- Myron Marston

I'd naively expect described_class to return "the class of the described thing" (i.e. Class when describing an actual class). Leaving open because this is definitely still an inconsistency.

-- Xavier Shay

There was an idea to change described_class's behaviour:

describe [1,2,3] do
  it { described_class.should eq(Array) }
  it { subject.should be_a(Array) }
end

Unfortunately, this idea was two weeks late for RSpec 3.0 release.

I like your idea, @betesh. I wish we had thought of it before 3.0 shipped.

-- Myron Marston

With all this confusion in mind:

I think in RSpec 4 we should just remove described_class, we kind of consider it an anti-pattern (because using it means the class must be loaded when the file is evaluated rather than when it's run) and we thus don't recommend it, and it causes this kind of ambiguity.

-- Jon Rowe

Bottom Line

Pros of described_class:

  • is widely used
  • reads nicely (when used properly)
  • reduces churn if the name of the class under test changes

Cons of described_class:

  • leads to confusion with nested example groups
  • leads to confusion when metadata is used without docstring
  • leads to confusion when something that is neither a String nor a Class is passed to example group as the first argument
  • might disappear in RSpec 4.0

How to Deal with This

What do you think about introducing a guideline to discourage passing anything than a literal class and a string as the first argument to an example group?
React with a 🚀 if you agree.

Or would you be radical and discourage the use of described_class altogether, and recommend using class names explicitly?
Place a 👍 if you decide to be explicit about what is being tested.

Please react with 👎 if you'd like to stick to described_class (at least until RSpec 4 is out).

Highly appreciate if you add additional arguments pro and contra.

Mention the built-in alternative to DatabaseCleaner for Rails

DatabaseCleaner was never meant to be used in Rails apps, and while still allowing the truncation and deletion strategies that some may find useful (I can't imagine in which cases though).

One of my motivations for writing this library was to have an easy way to turn on what Rails calls "transactional_fixtures" in my non-rails ActiveRecord projects.

Actually transactional_fixtures have nothing to do with fixtures, and have been recently renamed to transactional_tests to reflect what they are intended to be used for, wrap every example in a transaction and roll it back after example.

Originally found in https://anti-pattern.com/transactional-fixtures-in-rails

Use `before` and `let` when it's beneficial

Only use before and let, when there's a compelling benefit, don't use them as the default way to write specs.
https://reddit.com/r/ruby/comments/73nrhk/ama_the_authors_of_effective_testing_with_rspec_3/dnsyanp/

An example I can think of:

# bad
describe '#filename' do
  let(:filename) { 'input.csv' }

  before { settings.load!(filename) }

  specify do
    expect(settings.filename).to eq('input.csv')
  end
end

# good
describe '#filename' do
  it 'returns filename' do
    filename = 'input.csv'
    settings.load!(filename)

    expect(settings.filename).to eq('input.csv')
  end
end

Clean up the stale branches

Some of the branches contain meaningful commits. Consider picking them.
Drop the unused branches afterward.

Avoid using `before(:context)`

https://www.reddit.com/r/ruby/comments/73nrhk/ama_the_authors_of_effective_testing_with_rspec_3/dnt6vl1/?st=jottx59c&sh=932d4496

in practice it tends to cause problems for users because so many things in RSpec and the surrounding ecosystem assume a per-example lifecycle. For example, if you wrap each example in a DB transaction (as most projects do), and create some DB records in a before(:context) hook, you've just created the records outside of the transaction and those records will leak into other examples and possibly cause them to fail

Mention Crystallball

CrystallBall is the library implementing Regression Test Selection, helping you to only run the tests that are affected by the code changed. Might be a good fit for projects with large spec suites.

Discourage stubbing subject

Some times people stub helper methods in the object they are testing, but that's a test smell and often indicate a bad design on the object itself.

RSpec/SubjectStub cop flags allow and expect on the subject

Don't Recommend let or let!

Opening this per @bbatsov's response to my comment on Reddit.

The meat of my argument (as copied from here):

There's a comment from Myron Marston, long time RSpec maintainer explaining this...

I've seen Marston's arguments from 2011 and mostly have never agreed. Here ~7 years later in 2018, it seems he may be starting to realize they're bad:

My rule of thumb is to use them when there's a compelling benefit,...
... but don't reach for them as the default way to write specs.

No. There's never a compelling benefit: interpreter detecting your typo and lazy loading objects under test are not compelling.

If I was concerned about an interpreter detecting a typo I'd be using a different language.

Lazy loading, what is the point? If your tests are consuming resources to the point that you need to lazy evaluate them your tests have a problem.

Remember, we're talking maintenance phase here: people need to go back to tests written months and years ago. Tracing through lazy loaded let hierarchies sprinkled with eager loaded let! with before hooks and shared examples that depend on the caller to define more lets is not maintainable.

Obscure tests is not a compelling benefit.

And really, we're talking maintainability and style, so consistency is important. Let's throwout the very important argument above and consider this. It's safe to say (I think) that after 20+ years the TMTOWTDI camp has lost out to the There should be one—and preferably only one—obvious way to do it camp. I mean that's why we're here talking style guides, right? We don't want TMTOWTDI.

In this case not everything can be done with let, you need let!. And not everything can be done with let, you need before. Why have some tests use X and some use Y and some use X and Y and maybe Z? Be consistent. KISS: use before. It's all you need.

Recommend using `it_behaves_like` vs `include_examples`

The difference between the two is that the former creates a context, while the latter includes the examples and groups together with the context defined in the shared_examples that may lead to undesired consequences as overridden declarations.
I don't have good use cases for the latter in mind.

Encourage to use local matchers

E.g.

  matcher :be_just_like do |expected|
    match {|actual| actual == expected}
  end

Related docs:
https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/custom-matchers/define-a-custom-matcher#scoped-in-a-module
https://relishapp.com/rspec/rspec-expectations/v/3-8/docs/custom-matchers/define-a-custom-matcher#scoped-in-an-example-group

Caveats

I personally find it very confusing that those matchers don't have access to group variables, as opposed to ad-hoc methods:

def send_message
  have_attributes(channel: channel, text: text)
end

let(:channel) { '#announcements' }
let(:text) { 'Hello, world' }

it { is_expected.to send_message }

With a matcher:

let(:channel) { '#announcements' }
let(:text) { 'Hello, world' }

# works
matcher :send_message do |channel, text|
  values_match? actual, have_attributes(channel: channel, text: text)
end

it { is_expected.to send_message(channel, text) }

# fails with
#     undefined local variable or method `channel' for #<Class:#<RSpec::Matchers::DSL::Matcher:0x00007fb334216058>> 

matcher :send_message do
  values_match? actual, have_attributes(channel: channel, text: text)
end

it { is_expected.to send_message }

Remove mentions of tools?

I think mentioning workflow tools such as a guard and fuubar is out of scope for a style guide and should be removed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.