Slide 1

Slide 1 text

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