Slide 1

Slide 1 text

Test Your Legacy Code Noel Rappin @noelrap Monday, November 7, 11

Slide 2

Slide 2 text

You are handed a “rescue” project Monday, November 7, 11

Slide 3

Slide 3 text

Oh. My. God. The last person to work on this was an idiot Monday, November 7, 11

Slide 4

Slide 4 text

Now what? Monday, November 7, 11

Slide 5

Slide 5 text

Shake your fists at the heavens for one minute. Photo by Sha Sha Chu http://www.flickr.com/photos/shashachu/88654315/ Monday, November 7, 11

Slide 6

Slide 6 text

Get back to work. Monday, November 7, 11

Slide 7

Slide 7 text

What is a legacy application? Monday, November 7, 11

Slide 8

Slide 8 text

Code without tests Monday, November 7, 11

Slide 9

Slide 9 text

Code based on lost requirements Monday, November 7, 11

Slide 10

Slide 10 text

You don’t know what “correct” means Monday, November 7, 11

Slide 11

Slide 11 text

What’s your goal? Monday, November 7, 11

Slide 12

Slide 12 text

Respect working code. Do no harm. Monday, November 7, 11

Slide 13

Slide 13 text

Deliver features Monday, November 7, 11

Slide 14

Slide 14 text

At a sustainable pace Monday, November 7, 11

Slide 15

Slide 15 text

Improve code quality Monday, November 7, 11

Slide 16

Slide 16 text

The Boy Scout Rule Monday, November 7, 11

Slide 17

Slide 17 text

Our rescue project needs to change Monday, November 7, 11

Slide 18

Slide 18 text

Goofus and Gallant Monday, November 7, 11

Slide 19

Slide 19 text

“First, let’s write tests and cover the whole app” Monday, November 7, 11

Slide 20

Slide 20 text

“That change is easy, I’ll just drop that in” Monday, November 7, 11

Slide 21

Slide 21 text

They are both Goofus Monday, November 7, 11

Slide 22

Slide 22 text

Why not cover the whole app in tests? Monday, November 7, 11

Slide 23

Slide 23 text

“It’s a small world, but I wouldn’t want to paint it” Monday, November 7, 11

Slide 24

Slide 24 text

It’s not a quick win Monday, November 7, 11

Slide 25

Slide 25 text

You can introduce bugs Monday, November 7, 11

Slide 26

Slide 26 text

Why not just drop something in? Monday, November 7, 11

Slide 27

Slide 27 text

It sets a bad precedent Monday, November 7, 11

Slide 28

Slide 28 text

It doesn’t make anything better Monday, November 7, 11

Slide 29

Slide 29 text

What’s a Gallant Software Craftsman to do? Monday, November 7, 11

Slide 30

Slide 30 text

The basic TDD process... Monday, November 7, 11

Slide 31

Slide 31 text

...applied to new changes only Monday, November 7, 11

Slide 32

Slide 32 text

Red. Green. Refactor. Monday, November 7, 11

Slide 33

Slide 33 text

Over time, coverage and quality grow organically Monday, November 7, 11

Slide 34

Slide 34 text

Okay, it’s not that easy Monday, November 7, 11

Slide 35

Slide 35 text

Why not? Monday, November 7, 11

Slide 36

Slide 36 text

You don’t know what to test Monday, November 7, 11

Slide 37

Slide 37 text

It’s hard to isolate objects under test Monday, November 7, 11

Slide 38

Slide 38 text

A friendly reminder: git is your friend Monday, November 7, 11

Slide 39

Slide 39 text

Cucumber Photo by karenandbradmerson http://www.flickr.com/photos/karenandbrademerson/492770419 Monday, November 7, 11

Slide 40

Slide 40 text

Cucumber is independent of the code Monday, November 7, 11

Slide 41

Slide 41 text

Cucumber can cover a lot of the app quickly Monday, November 7, 11

Slide 42

Slide 42 text

But... Monday, November 7, 11

Slide 43

Slide 43 text

Cucumber is slow Photo by matley0 http://www.flickr.com/photos/matley0/2973398121/ Monday, November 7, 11

Slide 44

Slide 44 text

And it’s not great at isolating where a problem is Monday, November 7, 11

Slide 45

Slide 45 text

Test-Driven Exploration Monday, November 7, 11

Slide 46

Slide 46 text

It’s like TDD... with cheating Monday, November 7, 11

Slide 47

Slide 47 text

Red. Make it green. Refactor. Monday, November 7, 11

Slide 48

Slide 48 text

The code is source of truth Monday, November 7, 11

Slide 49

Slide 49 text

Mock Testing Monday, November 7, 11

Slide 50

Slide 50 text

Mocks are fast and allow unit testing Monday, November 7, 11

Slide 51

Slide 51 text

But they can be hard to set up Monday, November 7, 11

Slide 52

Slide 52 text

Isolation Monday, November 7, 11

Slide 53

Slide 53 text

Only touch existing methods to call your new methods Monday, November 7, 11

Slide 54

Slide 54 text

def crazy_insane_method return clean_new_method if new_condition # all the old ugly stuff end Monday, November 7, 11

Slide 55

Slide 55 text

Seams Photo by boogah http://www.flickr.com/photos/boogah/4541496/ Monday, November 7, 11

Slide 56

Slide 56 text

Default method arguments Monday, November 7, 11

Slide 57

Slide 57 text

def crazy_insane_method(arg, arg, new_arg = nil) # crazy insane stuff # now with new stuff end Monday, November 7, 11

Slide 58

Slide 58 text

Changing behavior without touching code Monday, November 7, 11

Slide 59

Slide 59 text

Monday, November 7, 11

Slide 60

Slide 60 text

class TestPurchase < Purchase def bang # no-op end end user.purchase << TestPurchase.new Monday, November 7, 11

Slide 61

Slide 61 text

class Purchase def bang # no-op end end Monday, November 7, 11

Slide 62

Slide 62 text

Singleton classes Monday, November 7, 11

Slide 63

Slide 63 text

x = Purchase.new x.def bang # no-op end Monday, November 7, 11

Slide 64

Slide 64 text

Duck Typing Photo by Andyofne http://www.flickr.com/photos/andyofne/3757051334/ Monday, November 7, 11

Slide 65

Slide 65 text

class TestPurchase def bang # no-op end end user.purchase << TestPurchase.new Monday, November 7, 11

Slide 66

Slide 66 text

Use a pebble Photo by Slideshow Bruce http://www.flickr.com/photos/springfieldhomer/39079430/ Monday, November 7, 11

Slide 67

Slide 67 text

class Pebble def initialize(name) @name = name end def method_missing(method_name, *args) p "#{method_name}(#{args.join(", ")}) from #{caller_method}" self end end Monday, November 7, 11

Slide 68

Slide 68 text

def caller_method(depth = 1) parse_caller(caller(depth+1).first).last end def parse_caller(at) if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at file = Regexp.last_match[1] line = Regexp.last_match[2].to_i method = Regexp.last_match[3] [file, line, method] end Monday, November 7, 11

Slide 69

Slide 69 text

What about bad tests? Monday, November 7, 11

Slide 70

Slide 70 text

Make sure the suite runs Monday, November 7, 11

Slide 71

Slide 71 text

Tests are also code Monday, November 7, 11

Slide 72

Slide 72 text

The 5-minute rule Monday, November 7, 11

Slide 73

Slide 73 text

When to refactor Monday, November 7, 11

Slide 74

Slide 74 text

Simple refactors can be done at any time... Monday, November 7, 11

Slide 75

Slide 75 text

...as long as the tests are green Monday, November 7, 11

Slide 76

Slide 76 text

Especially if you can’t test without refactoring Monday, November 7, 11

Slide 77

Slide 77 text

More complex refactors should be tied to actual new requests Monday, November 7, 11

Slide 78

Slide 78 text

And in conclusion... Monday, November 7, 11

Slide 79

Slide 79 text

Respect working code Monday, November 7, 11

Slide 80

Slide 80 text

Test at as high a level as you can Monday, November 7, 11

Slide 81

Slide 81 text

Leave things better than you found them Monday, November 7, 11

Slide 82

Slide 82 text

Isolate code under test Monday, November 7, 11

Slide 83

Slide 83 text

Consider the cost of change against the cost of no change Monday, November 7, 11

Slide 84

Slide 84 text

railsrx.com @noelrap Rails Test Prescriptions Rate: http://bit.ly/nrmwtest Monday, November 7, 11