Tests for Design
Tests for Security
Chris Keathley / @ChrisKeathley / keathley.io
Slide 2
Slide 2 text
Who is doing TDD?
Slide 3
Slide 3 text
Who likes doing
TDD?
Slide 4
Slide 4 text
Change
Slide 5
Slide 5 text
Change is
hard
Slide 6
Slide 6 text
Change is
inevitable
Slide 7
Slide 7 text
Change is
good
Slide 8
Slide 8 text
Change is
our responsibility
Slide 9
Slide 9 text
Design
Slide 10
Slide 10 text
Design is
hard
Slide 11
Slide 11 text
Design must be
flexible
Slide 12
Slide 12 text
Design for
purpose
Slide 13
Slide 13 text
Tests
Slide 14
Slide 14 text
I love tests
Slide 15
Slide 15 text
Zen
Slide 16
Slide 16 text
Tests provide guard
rails.
Slide 17
Slide 17 text
Focus
Slide 18
Slide 18 text
Empower
Refactoring
Slide 19
Slide 19 text
“Refactoring without tests
is just moving crap around”
Slide 20
Slide 20 text
“Refactoring without tests
is just moving crap around”
- me (just now)
Slide 21
Slide 21 text
Protection from
Regression
Slide 22
Slide 22 text
Protip:
Managers love the
idea of security
Slide 23
Slide 23 text
There are bugs in
your code.
Slide 24
Slide 24 text
All of this is good
Slide 25
Slide 25 text
But is isn’t
everything
Slide 26
Slide 26 text
Test Driven
Design
Slide 27
Slide 27 text
Lets talk about…
Slide 28
Slide 28 text
Lets talk about…
TDD 101
Slide 29
Slide 29 text
Lets talk about…
TDD 101
Commands and Queries
Slide 30
Slide 30 text
Lets talk about…
TDD 101
Commands and Queries
Designing Apis
Slide 31
Slide 31 text
Lets talk about…
TDD 101
Commands and Queries
Designing Apis
Test Speed
Slide 32
Slide 32 text
A note for those who are feeling pedantic
Slide 33
Slide 33 text
“Any sufficiently complicated test
suite contains an ad hoc, informally-
specified, bug-ridden, slow
implementation of half of Erlang”
- also me (also just now)
Slide 34
Slide 34 text
TDD 101
Slide 35
Slide 35 text
Methodology
Slide 36
Slide 36 text
Methodology
Write a failing test
Slide 37
Slide 37 text
Methodology
Write a failing test
Make that test pass
Slide 38
Slide 38 text
Methodology
Write a failing test
Make that test pass
Refactor
Slide 39
Slide 39 text
Methodology
Write a failing test
Make that test pass
Refactor
Slide 40
Slide 40 text
describe Platypus do
it "can drink" do
end
end
Slide 41
Slide 41 text
describe Platypus do
it "can drink" do
expect(platypus.stomach).to include coffee
end
end
Slide 42
Slide 42 text
describe Platypus do
it "can drink" do
platypus.drink(coffee)
expect(platypus.stomach).to include coffee
end
end
Slide 43
Slide 43 text
describe Platypus do
it "can drink" do
platypus = Platypus.new
coffee = Coffee.new(grams: 400)
platypus.drink(coffee)
expect(platypus.stomach).to include coffee
end
end
Slide 44
Slide 44 text
We know too much
implementation
Slide 45
Slide 45 text
describe Platypus do
it "can drink" do
platypus = Platypus.new
coffee = Coffee.new(grams: 400)
platypus.drink(coffee)
expect(platypus.stomach).to include coffee
end
end
Exposes Data
Slide 46
Slide 46 text
describe Platypus do
it "can drink" do
platypus = Platypus.new
coffee = Coffee.new(grams: 400)
platypus.drink(coffee)
expect(platypus.drank?(coffee)).to eql(true)
end
end
Slide 47
Slide 47 text
class Platypus
def initialize
@stomach = []
end
def drink(coffee)
@stomach << coffee
end
def drank?(coffee)
@stomach.include?(coffee)
end
end
Slide 48
Slide 48 text
Rely on interfaces
and hide state
Slide 49
Slide 49 text
But is this the right
interface?
Slide 50
Slide 50 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
end
end
end
Slide 51
Slide 51 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
expect(platypus.energized?).to eql(true)
end
end
end
Slide 52
Slide 52 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
expect(platypus.energized?).to eql(true)
end
end
end
Slide 53
Slide 53 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
platypus = Platypus.new
expect(platypus.energized?).to eql(true)
end
end
end
Slide 54
Slide 54 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
platypus = Platypus.new
platypus.drink(Coffee.new(grams: 400))
expect(platypus.energized?).to eql(true)
end
end
end
Slide 55
Slide 55 text
class Platypus
def initialize
@stomach = []
end
def drink(coffee)
@stomach << coffee
end
def energized?
@stomach.any?
end
end
Slide 56
Slide 56 text
Think about
your interfaces
Slide 57
Slide 57 text
Test your
boundaries
Slide 58
Slide 58 text
Commands &
Queries
Slide 59
Slide 59 text
OO is about
messages
Slide 60
Slide 60 text
Object
Slide 61
Slide 61 text
Object
Queries
Slide 62
Slide 62 text
Object
Queries Commands
Slide 63
Slide 63 text
Queries
Commands
Return things
Change things
Slide 64
Slide 64 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
platypus = Platypus.new
platypus.drink(Coffee.new(grams: 400))
expect(platypus.energized?).to eql(true)
end
end
end
Slide 65
Slide 65 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
platypus = Platypus.new
platypus.drink(Coffee.new(grams: 400))
expect(platypus.energized?).to eql(true)
end
end
end
Query
Slide 66
Slide 66 text
describe Platypus do
describe "when it drinks coffee" do
it "becomes energized" do
platypus = Platypus.new
platypus.drink(Coffee.new(grams: 400))
expect(platypus.energized?).to eql(true)
end
end
end
Command
Query
Slide 67
Slide 67 text
Designing Apis -
Queries
Slide 68
Slide 68 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
end
end
end
Slide 69
Slide 69 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
expect(platypus.energy).to eql(45)
end
end
end
Slide 70
Slide 70 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
platypus = Platypus.new
expect(platypus.energy).to eql(45)
end
end
end
Slide 71
Slide 71 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
platypus = Platypus.new
platypus.drink(coffee)
expect(platypus.energy).to eql(45)
end
end
end
Slide 72
Slide 72 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
platypus = Platypus.new
coffee = double(energy: 50)
platypus.drink(coffee)
expect(platypus.energy).to eql(45)
end
end
end
Slide 73
Slide 73 text
describe Platypus do
describe "when it drinks coffee" do
it "gains energy" do
platypus = Platypus.new
coffee = double(energy: 50)
platypus.drink(coffee)
expect(platypus.energy).to eql(45)
end
end
end
Assert the result
Slide 74
Slide 74 text
class Platypus
def initialize
@stomach = []
end
def drink(coffee)
@stomach << coffee
end
def energy
@stomach
.map { |item| item.energy }
.reduce { |acc, i| acc + i }
end
end
Slide 75
Slide 75 text
class Platypus
def initialize
@stomach = []
end
def drink(coffee)
@stomach << coffee
end
def energy
@stomach.map(&:energy).reduce(:+) * 0.90
end
end
Slide 76
Slide 76 text
Design
collaborating
interfaces
Slide 77
Slide 77 text
Interfaces MUST be
enforced
Slide 78
Slide 78 text
describe Coffee do
describe "#energy" do
it "calculates the energy per gram" do
coffee = Coffee.new(grams: 400)
expect(coffee.energy).to eql(115)
end
end
end
Slide 79
Slide 79 text
class Coffee
def initialize(grams:)
@grams = grams
end
def energy
((@grams / 17.42) * 5).round
end
end
Slide 80
Slide 80 text
Keep interfaces
flexible
Slide 81
Slide 81 text
class Bacon
def initialize(grams:)
@grams = grams
end
def energy
9001
end
end
Slide 82
Slide 82 text
Queries
Assert only the reponse.
Slide 83
Slide 83 text
Queries
Don’t expect external
calls
Slide 84
Slide 84 text
Queries
Provide doubles if the
interface is unknown or
takes too long to build.
Slide 85
Slide 85 text
Designing Apis -
Commands
Slide 86
Slide 86 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
end
end
end
Slide 87
Slide 87 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
expect(shoes).to receive(:wear)
end
end
end
Slide 88
Slide 88 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
shoes = Shoes.new
expect(shoes).to receive(:wear)
end
end
end
Slide 89
Slide 89 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
shoes = Shoes.new
expect(shoes).to receive(:wear)
platypus.waddle!
end
end
end
Slide 90
Slide 90 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
shoes = Shoes.new
platypus = Platypus.new(shoes: shoes)
expect(shoes).to receive(:wear)
platypus.waddle!
end
end
end
Slide 91
Slide 91 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
shoes = Shoes.new
platypus = Platypus.new(shoes: shoes)
expect(shoes).to receive(:wear)
platypus.waddle!
end
end
end
Outgoing
Command
Slide 92
Slide 92 text
class Platypus
def initialize(shoes:)
@shoes = shoes
@stomach = []
end
def waddle!
@shoes.wear
end
end
Slide 93
Slide 93 text
class Platypus
def initialize(shoes:)
@shoes = shoes
@stomach = []
end
def waddle!
@shoes.wear
end
end
Dependency Injection
Slide 94
Slide 94 text
describe Platypus do
describe "when it waddles" do
it "wears out its shoes" do
shoes = Shoes.new
platypus = Platypus.new(shoes: shoes)
expect(shoes).to receive(:wear)
platypus.waddle!
expect(platypus.shod?).to eql(false)
end
end
end
Slide 95
Slide 95 text
class Platypus
def initialize(shoes:)
@shoes = shoes
@stomach = []
end
def waddle!
@shoes.wear
end
def shod?
@shoes.good_condition?
end
end
Slide 96
Slide 96 text
class Shoes
def initialize
@worn = false
end
def wear
@word = true
end
def good_condition?
!@worn
end
end
Slide 97
Slide 97 text
Expect outgoing
commands to be
sent
Slide 98
Slide 98 text
Commands
Assert all side effects
Slide 99
Slide 99 text
Commands
Inject dependencies
Slide 100
Slide 100 text
Commands
Mock outgoing calls
Slide 101
Slide 101 text
Test Speed
Slide 102
Slide 102 text
Fast tests are
important
Slide 103
Slide 103 text
TDD requires
feedback
Slide 104
Slide 104 text
Isolate from other
dependencies
Slide 105
Slide 105 text
Test at the lowest
possible level
Slide 106
Slide 106 text
Write fewer tests!
Slide 107
Slide 107 text
Conclusion
Slide 108
Slide 108 text
Test Interfaces
Slide 109
Slide 109 text
Test once
Slide 110
Slide 110 text
Test everything
Slide 111
Slide 111 text
Test for design
Slide 112
Slide 112 text
Security will follow
Slide 113
Slide 113 text
Prepare for
change
Slide 114
Slide 114 text
Thanks!
Chris Keathley / @ChrisKeathley / keathley.io