= RSpec Style Guide :idprefix: :idseparator: - :sectanchors: :sectlinks: :toc: preamble :toclevels: 1 ifndef::backend-pdf[] :toc-title: pass:[Table of Contents] endif::[] :source-highlighter: rouge
== Introduction
[quote, Officer Alex J. Murphy / RoboCop]
Role models are important.
ifdef::env-github[] TIP: You can find a beautiful version of this guide with much improved navigation at https://rspec.rubystyle.guide. endif::[]
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.
https://github.com/rubocop/rubocop[RuboCop], a static code analyzer (linter) and formatter, has a https://github.com/rubocop/rubocop-rspec[`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 https://asciidoctor.org/docs/asciidoctor-pdf/[AsciiDoctor PDF], and an HTML copy https://asciidoctor.org/docs/convert-documents/#converting-a-document-to-html[with] https://asciidoctor.org/#installation[AsciiDoctor] using the following commands:
asciidoctor-pdf -a allow-uri-read README.adoc
Install the rouge
gem to get nice syntax highlighting in the generated document.
====
== How to Read This Guide
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.
== A Living Document
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.
== Layout
=== Empty Lines inside Example Group[[empty-lines-after-describe]]
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.
describe Article do
describe '#summary' do
context 'when there is a summary' do
it 'returns the summary' do
# ...
end
end
end end
=== Empty Line between Example Groups [[empty-lines-between-describes]]
Leave one empty line between feature
, context
or describe
blocks.
Do not leave empty line after the last such block in a group.
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
describe Article do describe '#summary' do context 'when there is a summary' do # ... end
context 'when there is no summary' do
# ...
end
end
=== Empty Line After let
[[empty-lines-after-let]]
Leave one empty line after let
, subject
, and before
/after
blocks.
describe Article do subject { FactoryBot.create(:some_article) } describe '#summary' do # ... end end
describe Article do subject { FactoryBot.create(:some_article) }
=== Let Grouping
Only group let
, subject
blocks and separate them from before
/after
blocks.
It makes the code much more readable.
describe Article do subject { FactoryBot.create(:some_article) } let(:user) { FactoryBot.create(:user) } before do # ... end after do # ... end describe '#summary' do # ... end end
describe Article do subject { FactoryBot.create(:some_article) } let(:user) { FactoryBot.create(:user) }
before do # ... end
after do # ... end
=== Empty Lines around Examples[[empty-lines-around-it]]
Leave one empty line around it
/specify
blocks. This helps to separate the expectations from their conditional logic (contexts for instance).
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
describe '#summary' do let(:item) { double('something') }
it 'returns the summary' do # ... end
it 'does something else' do # ... end
== Example Group Structure
=== Leading subject
When subject
is used, it should be the first declaration in the example group.
describe Article do before do # ... end
let(:user) { FactoryBot.create(:user) } subject { FactoryBot.create(:some_article) }
describe '#summary' do # ... end end
describe Article do subject { FactoryBot.create(:some_article) } let(:user) { FactoryBot.create(:user) }
before do # ... end
=== Declaring subject
, let!
/let
and before
/after
hooks
When declaring subject
, let!
/let
and before
/after
hooks they should be in the following order:
subject
let!
/let
before
/after
describe Article do before do # ... end
after do # ... end
let(:user) { FactoryBot.create(:user) } subject { FactoryBot.create(:some_article) }
describe '#summary' do # ... end end
describe Article do subject { FactoryBot.create(:some_article) } let(:user) { FactoryBot.create(:user) }
before do # ... end
after do # ... end
=== Use Contexts
Use contexts to make the tests clear, well organized, and easy to read.
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
context 'when logged in' do it { is_expected.to respond_with 200 } end
=== Context Cases
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.
describe '#attributes' do context 'the returned hash' do it 'includes the display name' do # ... end
it 'includes the creation time' do
# ...
end
end end
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
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) }
=== let
Blocks
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 let
s for primitive data, find the balance between frequency of use and complexity of the definition.
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
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
=== Instance Variables
Use let
definitions instead of instance variables.
before { @name = 'John Wayne' }
it 'reverses a name' do expect(reverser.reverse(@name)).to eq('enyaW nhoJ') end
let(:name) { 'John Wayne' }
=== Shared Examples
Use shared examples to reduce code duplication.
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
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
describe 'GET /devices' do let(:resource) { FactoryBot.create(:device, created_from: user) }
=== Redundant before(:each)
Don't specify :each
/:example
scope for before
/after
/around
blocks, as it is the default.
Prefer :example
when explicitly indicating the scope.
describe '#summary' do before(:example) do # ... end
end
describe '#summary' do before do # ... end
=== Ambiguous Hook Scope
Use :context
instead of the ambiguous :all
scope in before
/after
hooks.
describe '#summary' do before(:all) do # ... end
end
describe '#summary' do before(:context) do # ... end
=== Avoid Hooks with :context
Scope
Avoid using before
/after
with :context
scope.
Beware of the state leakage between the examples.
== Example Structure
=== Expectation per Example[[one-expectation]]
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.
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
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
=== Subject
When several tests relate to the same subject, use subject
to reduce repetition.
it { expect(hero.equipment).to be_heavy } it { expect(hero.equipment).to include 'sword' }
subject(:equipment) { hero.equipment }
=== Named Subject [[use-subject]]
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.
describe Article do subject { FactoryBot.create(:article) }
it 'is not published on creation' do expect(subject).not_to be_published end end
describe Article do subject { FactoryBot.create(:article) }
it 'is not published on creation' do is_expected.not_to be_published end end
describe Article do subject(:article) { FactoryBot.create(:article) }
=== Subject Naming in Context
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.
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
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
=== Don't Stub Subject
Don't stub methods of the object under test, it's a code smell and often indicates a bad design of the object itself.
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
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
describe 'Article' do subject(:presenter) { ArticlePresenter.new(article) } let(:article) { Article.new }
=== it
and specify
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
.
it do
end
specify 'it sends an email' do
end
specify { is_expected.to be_truthy }
it '#do_something is deprecated' do ... end
specify do
end
it 'sends an email' do
end
it { is_expected.to be_truthy }
=== it
in Iterators
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.
[:new, :show, :index].each do |action| it 'returns 200' do get action expect(response).to be_ok end end
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
=== Incidental State
Avoid incidental state as much as possible.
it 'publishes the article' do article.publish
expect(Article.count).to eq(2) end
=== DRY
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.
=== Factories
Use https://github.com/thoughtbot/factory_bot[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.
subject(:article) do Article.create( title: 'Piccolina', author: 'John Archer', published_at: '17 August 2172', approved: true ) end
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.
=== Needed Data
Do not load more data than needed to test your code.
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 }
=== Doubles
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.
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')
article = object_double(Article.new, valid?: true) expect(article.save).to be true
allow(Article).to receive(:find).with(5).and_return(article)
NOTE: If you stub a method that could give a false-positive test result, you have gone too far.
=== Dealing with Time
Always use https://github.com/travisjeffery/timecop[Timecop] instead of stubbing anything on Time or Date.
describe InvoiceReminder do subject(:time_with_offset) { described_class.new.get_offset_time }
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
=== Stub HTTP Requests
Stub HTTP requests when the code is making them. Avoid hitting real external services.
Use https://github.com/bblimke/webmock[webmock] and https://github.com/vcr/vcr[VCR] separately or https://marnen.github.io/webmock-presentation/webmock.html[together].
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')) }
[#declare-constants] === Declare Constants
Do not explicitly declare classes, modules, or constants in example groups. https://rspec.info/features/3-12/rspec-mocks/mutating-constants/[Stub constants instead].
NOTE: Constants, including classes and modules, when declared in a block scope, are defined in global namespace, and leak between examples.
describe SomeClass do CONSTANT_HERE = 'I leak into global namespace' end
describe SomeClass do before do stub_const('CONSTANT_HERE', 'I only exist during this example') end end
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
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
describe SomeClass do before do foo_class = Class.new(described_class) do def do_something end end stub_const('FooClass', foo_class) end
[#implicit-block-expectations] === Implicit Block Expectations
Avoid using implicit block expectations.
subject { -> { do_something } } it { is_expected.to change(something).to(new_value) }
== Naming
=== Context Descriptions
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.
describe 'Summary' do context 'user logged in' do context 'no display name' do it 'shows a placeholder' do end end end end
=== Example Descriptions
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
.
it 'returns the display name if it is present' do
end
context 'when display name is present' do it 'returns the display name' do # ... end end
=== Keep Example Descriptions Short
Keep example description shorter than 60 characters.
Write the example that documents itself, and generates proper documentation format output.
it 'rewrites "should not return something" as "does not return something"' do
end
it 'rewrites "should not return something"' do expect(rewrite('should not return something')).to eq 'does not return something' end
=== "Should" in Example Docstrings[[should-in-it]]
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.
it 'should return the summary' do
end
it 'returns the summary' do
=== Describe the Methods[[example-group-naming]]
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.
describe 'the authenticate method for User' do
end
describe 'if the user is an admin' do
end
describe '.authenticate' do
end
describe '#admin?' do
=== Use expect
Always use the newer expect
syntax.
Configure RSpec to only accept the new expect
syntax.
it 'creates a resource' do response.should respond_with_content_type(:json) end
== Matchers
=== Predicate Matchers
Use RSpec's predicate matcher methods when possible.
describe Article do subject(:article) { FactoryBot.create(:article) }
it 'is published' do expect(article.published?).to be true end
it 'is published' do expect(article).to be_published end
=== Built in Matchers
Use built-in matchers.
it 'includes a title' do expect(article.title.include?('a lengthy title')).to be true end
=== be
Matcher
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.
it 'has author' do expect(article.author).to be end
be
is often used to check for non-nil value=== Extract Common Expectation Parts into Matchers
Extract frequently used common logic from your examples into https://rspec.info/features/3-12/rspec-expectations/custom-matchers/define-matcher/[custom matchers].
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
it 'returns JSON with temperature in Celsius' do expect(response).to include_json(celsius: 30) end
=== any_instance_of
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.
it 'has a name' do allow_any_instance_of(User).to receive(:name).and_return('Tweedledee') expect(account.name).to eq 'Tweedledee' end
let(:account) { Account.new(user) }
=== Matcher Libraries
Use third-party matcher libraries that provide convenience helpers that will significantly simplify the examples, https://github.com/thoughtbot/shoulda-matchers[Shoulda Matchers] are one worth mentioning.
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
== Rails: Integration[[integration]][[rails]]
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.
== Rails: Views[[views]]
=== View Directory Structure
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
.
=== View Spec File Name
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
.
=== View Outer describe
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.
describe 'articles/new.html.erb' do
=== View Mock Models
Always mock the models in the view specs. The purpose of the view is only to display information.
=== View assign
The method assign
supplies the instance variables which the view uses and are supplied by the controller.
=== Capybara Negative Selectors[[view-capybara-negative-selectors]]
Prefer capybara negative selectors over to_not
with positive ones.
expect(page).to_not have_selector('input', type: 'submit') expect(page).to_not have_xpath('tr')
=== View Helper Stub
When a view uses helper methods, these methods need to be stubbed.
Stubbing the helper methods is done on the template
object:
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')
=== View Helpers
The helpers specs are separated from the view specs in the spec/helpers
directory.
== Rails: Controllers[[controllers]]
=== Controller Models
Mock the models and stub their methods. Testing the controller should not depend on the model creation.
=== Controller Behaviour
Test only the behaviour the controller should be responsible about:
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, 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
=== Controller Contexts
Use context when the controller action has different behaviour depending on the received params.
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
== Rails: Models[[models]]
=== Model Mocks
Do not mock the models in their own specs.
=== Model Objects
Use FactoryBot.create
to make real objects, or just use a new (unsaved) instance with subject
.
describe Article do subject(:article) { FactoryBot.create(:article) }
=== Model Mock Associations
It is acceptable to mock other models or child objects.
=== Avoid Duplication in Model Tests[[model-avoid-duplication]]
Create the model for all examples in the spec to avoid duplication.
=== Check Model Validity[[model-check-validity]]
Add an example ensuring that the model created with FactoryBot.create
is valid.
=== Model Validations
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.
describe '#title' do it 'is required' do article.title = nil expect(article).to_not be_valid end end
=== Separate Example Group for Attribute Validations[[model-separate-describe-for-attribute-validations]]
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
=== Naming Another Object[[model-name-another-object]]
When testing uniqueness of a model attribute, name the other object another_object
.
== Rails: Mailers[[mailers]]
=== Mailer Mock Model
The model in the mailer spec should be mocked. The mailer should not depend on the model creation.
=== Mailer Expectations
The mailer spec should verify that:
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
== Recommendations
=== Correct Setup
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.
--color --require spec_helper
== Related Guides
== Contributing
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 https://www.patreon.com/bbatsov[Patreon].
=== How to Contribute?
It's easy, just follow the contribution guidelines below:
== License
image:https://i.creativecommons.org/l/by/3.0/88x31.png[Creative Commons License] This work is licensed under a http://creativecommons.org/licenses/by/3.0/deed.en_US[Creative Commons Attribution 3.0 Unported License]
== Credit
Inspiration was taken from the following:
https://github.com/howaboutwe/rspec-style-guide[HowAboutWe's RSpec style guide]
https://github.com/rubocop/rails-style-guide[Community Rails style guide]
This guide was maintained by https://github.com/reachlocal[ReachLocal] for a long while.
This guide includes material originally present in https://github.com/betterspecs/betterspecs[BetterSpecs] (https://betterspecs.github.io/betterspecs/[newer site] https://www.betterspecs.org/[older site]), sponsored by https://github.com/lelylan[Lelylan] and maintained by https://github.com/andreareginato[Andrea Reginato] and https://github.com/betterspecs/betterspecs/graphs/contributors[many others] for a long while.