Slide 1

Slide 1 text

Testing Magic by Luís Zamith

Slide 2

Slide 2 text

@zamith

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Test Driven Development A guy called Kent Beck came up with a different way of writing code, and called it

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

it takes too much TIME

Slide 7

Slide 7 text

it takes too much TIME need to get SH*T DONE

Slide 8

Slide 8 text

it takes too much TIME need to get SH*T DONE tests are EXPENSIVE

Slide 9

Slide 9 text

Productivity Time w/o tests with tests *completely fictional data

Slide 10

Slide 10 text

Fixing bugs over and over again Large classes and methods Code is a mess Fear of change Hard to refactor

Slide 11

Slide 11 text

Fixing bugs over and over again Large classes and methods Code is a mess Fear of change Hard to refactor these are symptoms of bad practices

Slide 12

Slide 12 text

TDD is NOT the cure!

Slide 13

Slide 13 text

TDD is NOT the cure! But it sure helps...

Slide 14

Slide 14 text

RED GREEN REFACTOR

Slide 15

Slide 15 text

RED GREEN REFACTOR do the smallest step you can

Slide 16

Slide 16 text

RED GREEN REFACTOR stay here as little as possible do the smallest step you can

Slide 17

Slide 17 text

RED GREEN REFACTOR stay here as little as possible do the smallest step you can go drink a beer

Slide 18

Slide 18 text

- More focused classes - Looser coupling - Cleaner interfaces - More confidence in your code you get: you don’t get: - Bug free code - The best code in the world

Slide 19

Slide 19 text

Not allowed to write: - code unless it is to make a test pass - more of a test than is sufficient to fail - more code than is sufficient to pass the test BOB MARTIN picture source: http://agile2012.agilealliance.org

Slide 20

Slide 20 text

It’s your job as a developer to write your own tests.

Slide 21

Slide 21 text

There is more to testing than TDD, go check it out so you can chose your way...

Slide 22

Slide 22 text

magic tricks of testing Testing is hard, and when you make a change that breaks half your tests you feel you’ve been wasting your time. Well, fear not young chap, and come learn some

Slide 23

Slide 23 text

I hate my tests

Slide 24

Slide 24 text

I hate my tests Why do ?

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

They are slow

Slide 27

Slide 27 text

They are slow They are expensive

Slide 28

Slide 28 text

They are slow They are expensive They are fragile

Slide 29

Slide 29 text

They are slow They are expensive They are fragile

Slide 30

Slide 30 text

Your app is still a mess

Slide 31

Slide 31 text

THAT CAN CHANGE YOUR LIFE CAN BE SAVED!

Slide 32

Slide 32 text

OOP focuses on messages A B

Slide 33

Slide 33 text

Message origin SUT Sent to self Incoming Outgoing

Slide 34

Slide 34 text

Message type Query A B return something change nothing Command A B return nothing change something

Slide 35

Slide 35 text

What about methods that do both?

Slide 36

Slide 36 text

What about methods that do both? Separate them into two. SRP, remember?

Slide 37

Slide 37 text

Query Command Incoming Sent to self Outgoing Message type Message origin

Slide 38

Slide 38 text

Incoming Query Messages

Slide 39

Slide 39 text

class Player < Struct.new :height, :weight def body_mass_index (weight / height**2).round 1 end end object

Slide 40

Slide 40 text

class Player < Struct.new :height, :weight def body_mass_index (weight / height**2).round 1 end end describe Player do context "#body_mass_index" do it "calculates the bmi" do lebron = Player.new 2.03, 113 lebron.body_mass_index.should eq 27.4 end end end object test

Slide 41

Slide 41 text

ASSERTIONS Test incoming query messages by making about what they SEND BACK

Slide 42

Slide 42 text

Query Command Incoming Sent to self Outgoing Message type Message origin

Slide 43

Slide 43 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result

Slide 44

Slide 44 text

WARNING Test the interface not the implementation

Slide 45

Slide 45 text

Incoming Command Messages

Slide 46

Slide 46 text

class Player < Struct.new :height, :weight attr_reader :skill def skill=(skill_level) @skill = skill_level end end object

Slide 47

Slide 47 text

class Player < Struct.new :height, :weight attr_reader :skill def skill=(skill_level) @skill = skill_level end end describe Player do context "#skill=" do it "updates the skill level" do player.skill = 10 player.skill.should eq 10 end end end object test

Slide 48

Slide 48 text

ASSERTIONS Test incoming command messages by making about DIRECT PUBLIC SIDE EFFECTS

Slide 49

Slide 49 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result

Slide 50

Slide 50 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects

Slide 51

Slide 51 text

Sent to self Messages

Slide 52

Slide 52 text

object class Player < Struct.new :height, :weight def body_mass_index actual_bmi.round 1 end private def actual_bmi (weight / height**2) end end

Slide 53

Slide 53 text

object class Player < Struct.new :height, :weight def body_mass_index actual_bmi.round 1 end private def actual_bmi (weight / height**2) end end

Slide 54

Slide 54 text

object class Player < Struct.new :height, :weight def body_mass_index actual_bmi.round 1 end private def actual_bmi (weight / height**2) end end

Slide 55

Slide 55 text

object class Player < Struct.new :height, :weight def body_mass_index actual_bmi.round 1 end private def actual_bmi (weight / height**2) end end testing state is redundant

Slide 56

Slide 56 text

describe Player do context "#body_mass_index" do it "calculates the bmi" do lebron = Player.new 2.03, 113 lebron.should_receive(:actual_bmi).and_call_original lebron.body_mass_index.should eq 27.4 end end end test

Slide 57

Slide 57 text

describe Player do context "#body_mass_index" do it "calculates the bmi" do lebron = Player.new 2.03, 113 lebron.should_receive(:actual_bmi).and_call_original lebron.body_mass_index.should eq 27.4 end end end test Over specification: no safety and breaks on every change

Slide 58

Slide 58 text

Do not test private methods RULE

Slide 59

Slide 59 text

Do not test private methods RULE Break at your own risk if it saves $$ in development

Slide 60

Slide 60 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects

Slide 61

Slide 61 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects Ignore

Slide 62

Slide 62 text

Outgoing Query Messages

Slide 63

Slide 63 text

object class Player < Struct.new :height, :weight def on_the_team?(team) team.has_player? self end end

Slide 64

Slide 64 text

object class Player < Struct.new :height, :weight def on_the_team?(team) team.has_player? self end end Player on_the_team? has_player? outgoing incoming Team

Slide 65

Slide 65 text

test describe Player do context "#on_the_team?" do it "finds if player is on the team" do team = Team.new team.add_player player player.should be_on_the_team team team.players.should include player end end end

Slide 66

Slide 66 text

test Redundant: duplicates team’s tests describe Player do context "#on_the_team?" do it "finds if player is on the team" do team = Team.new team.add_player player player.should be_on_the_team team team.players.should include player end end end

Slide 67

Slide 67 text

test describe Player do context "#on_the_team?" do it "finds if player is on the team" do team = Team.new team.add_player player team.should_receive (:has_player?).and_call_original player.should be_on_the_team team end end end

Slide 68

Slide 68 text

test Over specification: adds costs and no benefits describe Player do context "#on_the_team?" do it "finds if player is on the team" do team = Team.new team.add_player player team.should_receive (:has_player?).and_call_original player.should be_on_the_team team end end end

Slide 69

Slide 69 text

Do not test outgoing query messages RULE

Slide 70

Slide 70 text

Do not test outgoing query messages RULE If the message has no visible side effects the sender should not test it

Slide 71

Slide 71 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects Ignore

Slide 72

Slide 72 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects Ignore

Slide 73

Slide 73 text

Outgoing Command Messages

Slide 74

Slide 74 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end

Slide 75

Slide 75 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new player.infiltrate_team team team.players.should include player end end end test

Slide 76

Slide 76 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new player.infiltrate_team team team.players.should include player end end end test depends on distant side effect

Slide 77

Slide 77 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new player.infiltrate_team team team.players.should include player end end end test not the player’s responsibility

Slide 78

Slide 78 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end

Slide 79

Slide 79 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end this message must be sent

Slide 80

Slide 80 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new team.should_receive(:add_player) player.infiltrate_team team end end end test this message must be sent

Slide 81

Slide 81 text

object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new team.should_receive(:add_player) player.infiltrate_team team end end end test this message must be sent depends on the interface

Slide 82

Slide 82 text

it’s the player responsibility to send add_player to the team object class Player < Struct.new :height, :weight def infiltrate_team(team) team.add_player self end end describe Player do context "#infiltrate_team" do it "adds player to team" do team = Team.new team.should_receive(:add_player) player.infiltrate_team team end end end test this message must be sent

Slide 83

Slide 83 text

EXPECT outgoing command messages to send

Slide 84

Slide 84 text

EXPECT outgoing command messages to send if side effects are stable and cheap, rule might be broken

Slide 85

Slide 85 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects Ignore

Slide 86

Slide 86 text

Query Command Incoming Sent to self Outgoing Message type Message origin Assert result Assert direct public side effects Ignore Expect to send

Slide 87

Slide 87 text

Ensure mocks are in sync with public API RULE

Slide 88

Slide 88 text

- Test everything, but only once - Test the interfaces - Trust collaborators - In doubt, go for simple SUMMARY

Slide 89

Slide 89 text

Thanks.

Slide 90

Slide 90 text

- https://speakerdeck.com/skmetz/magic-tricks-of-testing- railsconf?slide=0 - http://butunclebob.com/ ArticleS.UncleBob.TheThreeRulesOfTdd - http://blog.zamith.pt/blog/2013/04/10/understanding-the- heart-to-become-a-better-doctor/ - http://www.amazon.com/Clean-Code-Handbook-Software- Craftsmanship/dp/0132350882?tag=viglink127254-20 - http://www.amazon.com/dp/0321721330 - http://www.cleancoders.com - https://learn.thoughtbot.com/purchases/ d27fb3e007037d4ef543caf84d87ecc7 - http://www.amazon.com/RSpec-Book-Behaviour- Development-Cucumber/dp/1934356379 We stand on the shoulders of giants - http://www.amazon.com/Test-Driven- Development-By-Example/dp/0321146530? tag=giantrobotssm-20 - https://peepcode.com/products/rspec-i - https://peepcode.com/products/rspec-ii - http://robots.thoughtbot.com/post/23112388518/ types-of-coupling

Slide 91

Slide 91 text

Luís Zamith @zamith blog.zamith.pt