Skip to content

Commit

Permalink
add article for described class
Browse files Browse the repository at this point in the history
  • Loading branch information
glaucocustodio committed Aug 3, 2024
1 parent e7c4180 commit d833d15
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 66 deletions.
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
# Even Better Specs

Event Better Specs is an opinionated set of best practices to support the creation of tests that are easy to read and maintain.
[Event Better Specs](https://evenbetterspecs.github.io/) is an opinionated set of best practices to support the creation of tests that are easy to read and maintain.

## Installation

git clone [email protected]:evenbetterspecs/evenbetterspecs.github.io.git
cd evenbetterspecs
cd evenbetterspecs
bundle install
bundle exec jekyll serve

## Guiding principles

Our guidelines are based on two fundamental principles:

- tests must be [self-contained](https://thoughtbot.com/blog/the-self-contained-test/), not DRY
- tests should follow the [Arrange-Act-Assert](https://automationpanda.com/2020/07/07/arrange-act-assert-a-pattern-for-writing-good-tests/) pattern

## Acknowledgment

- [BetterSpecs](https://www.betterspecs.org/)
Expand Down
3 changes: 3 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ nav_external_links:
- title: Use contexts
url: "#use-contexts"
hide_icon: true
- title: Leverage described class
url: "#leverage-described-class"
hide_icon: true
- title: Short description
url: "#short-description"
hide_icon: true
Expand Down
12 changes: 6 additions & 6 deletions _includes/all_possible_cases.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ <h2 id="all-possible-cases">
</a>
</h2>

<p>Testing is a good practice, but if you do not test the edge cases, it will not be useful. Test valid, edge and invalid case. For example, consider the following action..</p>
<p>Testing is a good practice, but if you do not test the edge cases, it will not be useful. Test valid, edge and invalid cases.</p>

<p class="note alert">If you have way too many cases to test, it might be an indication your subject class is doing too much and must be break down into other classes.</p>

Expand All @@ -23,8 +23,8 @@ <h2 id="all-possible-cases">

<div class="bad">
{% highlight ruby %}
describe '#destroy' do
context 'when product exists' do
describe 'DELETE /:id' do
context 'when the product exists' do
it 'deletes the product' do
end
end
Expand All @@ -34,13 +34,13 @@ <h2 id="all-possible-cases">

<div class="good">
{% highlight ruby %}
describe '#destroy' do
context 'when product exists' do
describe 'DELETE /:id' do
context 'when the product exists' do
it 'deletes the product' do
end
end

context 'when product does not exist' do
context 'when the product does not exist' do
it 'raises 404' do
end
end
Expand Down
31 changes: 22 additions & 9 deletions _includes/avoid_hooks.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@ <h2 id="avoid-hooks">
</a>
</h2>

<p>Avoid hooks whenever possible since they tend to make your tests complicated over time.</p>
<p>Avoid hooks since they usually cause your tests to become more complex in the long run.</p>

<div class="bad">
{% highlight ruby %}
describe '#index' do
describe 'GET /' do
context 'when user is authenticated' do
before do
@user = create(:user)
sign_in @user
get profile_path
end

context 'when user is admin' do
context 'when user has a profile' do
it 'returns 200' do
create(:profile, user: @user)
expect(response.code).to eq('200')
end
end

context 'when user is not admin' do
it 'returns 401' do
context 'when user does not have a profile' do
it 'returns 404' do
expect(response.code).to eq('404')
end
end
end
Expand All @@ -32,19 +36,28 @@ <h2 id="avoid-hooks">

<div class="good">
{% highlight ruby %}
describe '#index' do
describe 'GET /' do
context 'when user is authenticated' do
context 'when user is admin' do
context 'when user has a profile' do
it 'returns 200' do
user = create(:user)
create(:profile, user: user)
sign_in user

get profile_path

expect(response.code).to eq('200')
end
end

context 'when user is not admin' do
it 'returns 401' do
context 'when user does not have a profile' do
it 'returns 404' do
user = create(:user)
sign_in user

get profile_path

expect(response.code).to eq('404')
end
end
end
Expand Down
10 changes: 5 additions & 5 deletions _includes/create_only_the_data_you_need.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ <h2 id="create-only-the-data-you-need">

<div class="bad">
{% highlight ruby %}
describe '#featured_product' do
describe '.featured_product' do
it 'returns the featured product' do
create_list(:product, 5)
product_featured = create(:product, featured: true)

expect(subject.featured_product).to eq(product_featured)
expect(described_class.featured_product).to eq(product_featured)
end
end
{% endhighlight %}
</div>

<div class="good">
{% highlight ruby %}
describe '#featured_product' do
describe '.featured_product' do
it 'returns the featured product' do
product_non_featured = create(:product, featured: false)
create(:product, featured: false)
product_featured = create(:product, featured: true)

expect(subject.featured_product).to eq(product_featured)
expect(described_class.featured_product).to eq(product_featured)
end
end
{% endhighlight %}
Expand Down
18 changes: 17 additions & 1 deletion _includes/describe.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ <h2 id="describe">
</a>
</h2>

<p>Be clear about what method you are describing. For instance, use the Ruby documentation convention of <code>.</code> when referring to a class method's name and <code>#</code> when referring to an instance method's name.</p>
<p>Be clear about what you are testing.</p>

<div class="bad">
{% highlight ruby %}
Expand All @@ -19,6 +19,8 @@ <h2 id="describe">
{% endhighlight %}
</div>

<p>Use the Ruby documentation convention of <code>.</code> when referring to a class method's name and <code>#</code> when referring to an instance method's name.</p>

<div class="good">
{% highlight ruby %}
describe User do
Expand All @@ -28,6 +30,20 @@ <h2 id="describe">
describe '#admin?' do
end
end
{% endhighlight %}
</div>

<p>In request tests, describe the methods and paths under test.</p>

<div class="good">
{% highlight ruby %}
describe UsersController, type: :request do
describe 'POST /' do
end

describe 'GET /' do
end
end
{% endhighlight %}
</div>
</article>
16 changes: 8 additions & 8 deletions _includes/dont_use_shared_examples.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ <h2 id="dont-use-shared-examples">
<div class="bad">
{% highlight ruby %}
shared_examples 'a normal dog' do
it { is_expected.to be_able_to_jump }
it { is_expected.to be_able_to_bark }
end

RSpec.describe Dog do
subject { described_class.new(able_to_jump?: true, able_to_bark?: true) }
describe Dog do
subject { described_class.new(able_to_bark?: true) }
it_behaves_like 'a normal dog'
end
{% endhighlight %}
</div>

<div class="good">
{% highlight ruby %}
RSpec.describe Dog do
it 'barks and jumps' do
subject = described_class.new(able_to_jump?: true, able_to_bark?: true)
describe Dog do
describe '#able_to_bark?' do
it 'barks' do
subject = described_class.new(able_to_bark?: true)

expect(subject).to be_able_to_jump
expect(subject).to be_able_to_bark
expect(subject.able_to_bark?).to eq(true)
end
end
end
{% endhighlight %}
Expand Down
14 changes: 8 additions & 6 deletions _includes/factories_not_fixtures.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ <h2 id="factories-not-fixtures">
</a>
</h2>

<p><a href="https://github.com/thoughtbot/factory_bot" target="_blank">Factories</a> are more flexible and easy to work with.</p>
<p><a href="https://github.com/thoughtbot/factory_bot" target="_blank">Factories</a> are more flexible and easier to work with.</p>

<div class="example">
{% highlight ruby %}
def name
def full_name
"#{first_name} #{last_name}"
end
{% endhighlight %}
Expand All @@ -18,8 +18,9 @@ <h2 id="factories-not-fixtures">
<div class="good">
{% highlight ruby %}
it 'returns the full name' do
user = create(:user, fist_name: 'Ayrton', last_name: 'Senna')
expect(user.name).to eq('Ayrton Senna')
user = create(:user, fist_name: 'Santos', last_name: 'Dumont')

expect(user.full_name).to eq('Santos Dumont')
end
{% endhighlight %}
</div>
Expand All @@ -29,8 +30,9 @@ <h2 id="factories-not-fixtures">
<div class="good">
{% highlight ruby %}
it 'returns the full name' do
user = build_stubbed(:user, fist_name: 'Ayrton', last_name: 'Senna')
expect(user.name).to eq('Ayrton Senna')
user = build_stubbed(:user, fist_name: 'Santos', last_name: 'Dumont')

expect(user.full_name).to eq('Santos Dumont')
end
{% endhighlight %}
</div>
Expand Down
2 changes: 1 addition & 1 deletion _includes/group_expectations.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ <h2 id="group-expectations">
</h2>

<p>
Having an <code>it</code> for each expectation can lead to a terrible test performance. Try to group expectations that
Having an <code>it</code> for each expectation can lead to a terrible test performance. Group expectations that
use a similar data setup to improve performance and make the tests more readable.
</p>

Expand Down
4 changes: 2 additions & 2 deletions _includes/instance_double_over_double.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h2 id="instance-double-over-double">
<div class="bad">
{% highlight ruby %}
it "passes" do
user = double(:user, name: "Ayrton Senna")
user = double(:user, name: "Gustavo Kuerten")
puts user.name
end
{% endhighlight %}
Expand All @@ -29,7 +29,7 @@ <h2 id="instance-double-over-double">
<div class="good">
{% highlight ruby %}
it "fails" do
user = instance_double(User, name: "Ayrton Senna")
user = instance_double(User, name: "Gustavo Kuerten")
puts user.name
end
{% endhighlight %}
Expand Down
22 changes: 12 additions & 10 deletions _includes/lets_not.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ <h2 id="lets-not">
</a>
</h2>

<p>Do not use <code>let</code> / <code>let!</code>. These tend to turn your tests very complicated over time as one needs to look up variables defined then apply deltas to figure their current state. Read more <a href="https://thoughtbot.com/blog/lets-not">here</a>.</p>
<p>Do not use <code>let</code> / <code>let!</code>. These tend to turn your tests very complicated over time as one needs to look up variables defined then apply deltas to figure their current state. Understand more <a href="https://thoughtbot.com/blog/lets-not">here</a>.</p>

<p class="note info">Tests are not supposed to be DRY, but easy to read and maintain.</p>

<div class="bad">
{% highlight ruby %}

describe '#name' do
let(:user) { create(:user, fist_name: 'Ayrton', last_name: 'Senna') }
describe '#full_name' do
let(:user) { create(:user, fist_name: 'Edson', last_name: 'Pelé') }

context 'when first name and last name are present' do
it 'returns the full name' do
expect(user.name).to eq('Ayrton Senna')
expect(user.full_name).to eq('Edson Pelé')
end
end

context 'when last name is not present' do
it 'returns the first name' do
user.last_name = nil
expect(user.name).to eq('Ayrton')
expect(user.full_name).to eq('Edson')
end
end
end
Expand All @@ -33,18 +33,20 @@ <h2 id="lets-not">

<div class="good">
{% highlight ruby %}
describe '#name' do
describe '#full_name' do
context 'when first name and last name are present' do
it 'returns the full name' do
user = create(:user, fist_name: 'Ayrton', last_name: 'Senna')
expect(user.name).to eq('Ayrton Senna')
user = create(:user, fist_name: 'Edson', last_name: 'Pelé')

expect(user.full_name).to eq('Edson Pelé')
end
end

context 'when last name is not present' do
it 'returns the first name' do
user = create(:user, fist_name: 'Ayrton', last_name: nil)
expect(user.name).to eq('Ayrton')
user = create(:user, fist_name: 'Edson', last_name: nil)

expect(user.full_name).to eq('Edson')
end
end
end
Expand Down
Loading

0 comments on commit d833d15

Please sign in to comment.