How I Learned to Stop
Worrying and Like
RSpec
Bryce Kerley
Miami Ruby Brigade
Sept. 15, 2014
Slide 2
Slide 2 text
Preemptive Answers
Yes, the slides will be online
Yes, the code samples are online
Slide 3
Slide 3 text
No content
Slide 4
Slide 4 text
Please Test Your
Important Code
Slide 5
Slide 5 text
I Like RSpec
• Testing, TDD, and Agile
• Minitest features
• How I got to RSpec
• RSpec philosophy
• RSpec practice
Slide 6
Slide 6 text
Agile
If you’re not sure what to do
yet, do the simplest thing
that could possibly work.
http://www.artima.com/intv/simplest3.html
Slide 7
Slide 7 text
Testing
Defines “work”
Slide 8
Slide 8 text
Testing
Tells you when
you break stuff
Slide 9
Slide 9 text
Forget Manual Testing
Error-prone
Doesn’t test all interactions
Especially ones that are a pain
Slide 10
Slide 10 text
Test-Driven Development
1. Write failing test for feature
2. Write code to pass the failing test
3. Write code to pass tests you broke
4. Clean up your mess
Slide 11
Slide 11 text
Test-driven development is about not being afraid to try something. If you break it, you’ll know, and you’re a `git checkout` away from fixing it.
Slide 12
Slide 12 text
I Like RSpec
• Testing, TDD, and Agile
• Minitest features
• How I got to RSpec
• RSpec philosophy
• RSpec practice
Slide 13
Slide 13 text
Conway's Game of Life
• Grid of cells
• Live cells with two or three neighbors stay alive
• Dead cells with three neighbors come to life
Slide 14
Slide 14 text
Five Minutes of Minitest
require 'minitest/autorun'
!
class CellTest < Minitest::Test
def test_counts_neighbors
c = Cell.new(0, 0)
assert_equal 0, c.neighbors
!
c2 = Cell.new(1, 0)
assert_equal 1, c.neighbors
end
!
def test_comes_to_life
end
def test_stays_alive
end
end
Slide 15
Slide 15 text
Minitest in Rails
require 'test_helper'
!
class PostTest < ActiveSupport::TestCase
test 'a post has many comments' do
p = Post.create
assert_empty p.comments
c = Comment.create post: p
refute_empty p.comments
end
end
Slide 16
Slide 16 text
Minitest with Nesting
Minitest::Test can’t
Minitest::Spec can
Slide 17
Slide 17 text
Minitest with Mocking
def test_comes_to_life
@board = Minitest::Mock.new
@board.expect :neighbors, 3
!
c = Cell.new(@board, 0, 0)
!
assert c.become_alive?
end
Slide 18
Slide 18 text
I Like RSpec
• Testing, TDD, and Agile
• Minitest features
• How I got to RSpec
• RSpec philosophy
• RSpec practice
Slide 19
Slide 19 text
basho
My Day Job
Three years at Basho!
We’re hiring!
Slide 20
Slide 20 text
Riak Ruby Client
• Started January 2010
• RSpec from the beginning
• I took over May 2013
• 5833 lines of RSpec tests
As of Thursday we have 5750 lines of tests, but we ripped out a bunch of complex systems.
Slide 21
Slide 21 text
No content
Slide 22
Slide 22 text
My Transition
Copying and modifying
existing specs
My first commit on riak-ruby-client had a zillion assignments in a `before` block.
Slide 23
Slide 23 text
My Transition
Finding good
documentation
It’s the rspec organization on “relishapp”, link later.
Slide 24
Slide 24 text
My Transition
Practice
and review
All my RSpec work was code-reviewed and commented on, which helped.
Slide 25
Slide 25 text
My Transition
Second
nature
Today, I’d rather write RSpec for new work even knowing my previous complaints about it.
Slide 26
Slide 26 text
I Like RSpec
• Testing, TDD, and Agile
• Minitest features
• How I got to RSpec
• RSpec philosophy
• RSpec practice
RSpec Philosophy
• Write yellow skeleton specs
• Implement an expectation so it’s red
• Fulfill an expectation to make it green
Slide 29
Slide 29 text
Conway's Game with RSpec
describe Cell do
subject { described_class.new(0, 0) }
!
it 'comes to life appropriately'
it 'stays alive appropriately’
!
it 'counts neighbors' do
expect(subject.neighbors).to eq 0
!
c2 = described_class.new(1, 0)
expect(subject.neighbors).to eq 1
end
end
Spec group
Skeletons
Spec
Expectation
Slide 30
Slide 30 text
RSpec in Rails
require 'rails_helper'
!
RSpec.describe Post, :type => :model do
subject{ described_class.create }
!
it 'has many comments' do
expect(subject.comments).to be_empty
c = Comment.create post: subject
expect(subject.comments).to_not be_empty
end
end
The Syntax
• A confusing mish-mash of spaces, underscores,
dots, parens, braces, symbols, strings
• Oh, and words
Slide 37
Slide 37 text
Let & Subject
describe “CRDTs" do
let(:bucket) { random_bucket }
!
describe 'counters' do
subject { Riak::Crdt::Counter.new bucket, random_key }
!
it 'asks for and accepts a returned body' do
other = Riak::Crdt::Counter.new(subject.bucket,
subject.key)
end
end
end
Slide 38
Slide 38 text
Let & Subject
• let defines a variable only when you need it
Slide 39
Slide 39 text
Let & Subject
class Turn
def initialize
puts "ACTUALLY BEING INITIALIZED"
end
end
!
describe Turn do
let(:turn){ Turn.new }
!
it "might not get initialized" do
expect(5).to eq 5
end
!
it "is only initialized on use" do
expect(turn).to be_a Turn
end
end
Slide 40
Slide 40 text
Let & Subject
• subject defines a variable named “subject” only
when you need it
• Is implicitly defined to described_class.new
• Allows implicit usage with it { is_expected… }
Slide 41
Slide 41 text
Let & Subject
class Board
def cromulent?
true
end
end
!
describe Board do
it { is_expected.to be }
it { is_expected.to be_a Board }
it { is_expected.to be_cromulent }
end
Slide 42
Slide 42 text
Quick Boolean Matchers
• Quickly verify any question? methods that take
no arguments:
• expect(my_object).to be_cromulent
• Passes if cromulent? returns true-y
Slide 43
Slide 43 text
Tagging & Focus
Tagging allows subsets of
tests to be run
Slide 44
Slide 44 text
Tagging & Focus
describe 'Secure Protobuffs’ do
!
describe 'without security enabled on Riak', no_security: true do
it 'connects normally without authentication configured' do
…
!
describe 'with security enabled on Riak', yes_security: true do
it 'connects normally with authentication configured' do
These tests are mutually exclusive; they require server settings to be toggled and local configuration settings to match. I use tags to control which set
runs.
Slide 45
Slide 45 text
Tagging & Focus
• focus is a tag with quick syntax
• skip is metadata with quick syntax
• “describe” to “fdescribe” or “xdescribe”
• “it” to “fit” or “xit”
• rspec ./spec/some_specs/… -t focus
Slide 46
Slide 46 text
Customizing & Promode
• Put a .rspec file in your project root
--format documentation --color
Slide 47
Slide 47 text
Promode
Slide 48
Slide 48 text
I Like RSpec
• Testing, TDD, and Agile
• Minitest features
• How I got to RSpec
• RSpec philosophy
• RSpec practice
Slide 49
Slide 49 text
Please Test
Test-driven
development makes
your project better
Slide 50
Slide 50 text
Minitest is Good
Minitest comes with Ruby and
it's good at getting the job
done
Slide 51
Slide 51 text
Bryce and RSpec
I started with RSpec because
I had to
I got good with RSpec
because I enjoyed it
Slide 52
Slide 52 text
RSpec Philosophy
Everything you need to test
behavior of objects
If it’s hard to do with RSpec, consider if it’s the right thing to do.
Slide 53
Slide 53 text
RSpec Practice
Start with skeletons
Describe behavior
Make it pass