Slide 1

Slide 1 text

welcome 1

Slide 2

Slide 2 text

Any code of your own that you haven't looked at for six or more months might as well have been written by someone else. — Eagleson's Law 2

Slide 3

Slide 3 text

legacy code 3

Slide 4

Slide 4 text

old interfaces 4

Slide 5

Slide 5 text

vulnerabilities 5

Slide 6

Slide 6 text

dead code 6

Slide 7

Slide 7 text

profit & users 7

Slide 8

Slide 8 text

developing legacy code is inevitable 8

Slide 9

Slide 9 text

tests 9

Slide 10

Slide 10 text

untested legacy code 10

Slide 11

Slide 11 text

types of tests 1.unit 2.API 3.UI 11

Slide 12

Slide 12 text

12

Slide 13

Slide 13 text

??? 13

Slide 14

Slide 14 text

observing change: a gold master test in practice jake @jwworth worth hashrocket 14

Slide 15

Slide 15 text

agenda 1.defining the gold master test 2.writing the test 3.working with the test 15

Slide 16

Slide 16 text

1. defining the gold master test 16

Slide 17

Slide 17 text

17

Slide 18

Slide 18 text

In nearly every legacy system, what the system does is more important than what it's supposed to do. — Michael C. Feathers, Working Effectively With Legacy Code, p. 186 18

Slide 19

Slide 19 text

characterization test A characterization test is a test that characterizes the actual behavior of a piece of code.1 1 Michael C. Feathers, Working Effectively With Legacy Code, pg. 195. 19

Slide 20

Slide 20 text

characterization test process1 1.Use a piece of code in a test harness. 2.Write an assertion that you know will fail. 3.Let the failure tell you what the behavior is. 4.Change the test so that it expects the behavior the code produces. 1 Michael C. Feathers, Working Effectively With Legacy Code, pg. 195. 20

Slide 21

Slide 21 text

# some/crummy_spec.rb expect(something).to eq 2 => Failure/Error: expect(something).to eq 2 expected: 2 got: 1 expect(something).to eq 1 => 1 example, 0 failures 21

Slide 22

Slide 22 text

a heuristic for writing characterization tests1 (abridged) 1.Write tests where you will make changes. Write as many cases as you feel you need. 2.Look at the specific things you are going to change, and attempt to write tests for those. 3.Write additional tests on a case-by-case basis. 1 Michael C. Feathers, Working Effectively With Legacy Code, pg. 195. 22

Slide 23

Slide 23 text

gold master test A gold master test is a regression test for complex, untested systems that asserts a consistent macro-level behavior. 23

Slide 24

Slide 24 text

first run 24

Slide 25

Slide 25 text

subsequent runs 25

Slide 26

Slide 26 text

ideal application • mature • complex • we expect minimal change to output 26

Slide 27

Slide 27 text

benefits • rigorous development standards • exposes surprising behavior • useful for forensic analysis 27

Slide 28

Slide 28 text

gold master test A gold master test is a regression test for complex, untested systems that asserts a consistent macro-level behavior. 28

Slide 29

Slide 29 text

2. writing the test 29

Slide 30

Slide 30 text

feature revisited • large production database • fed into a complex PostgreSQL function • output into a giant CSV file 30

Slide 31

Slide 31 text

testing writing phases i. preparation ii. test iii. evaluation 31

Slide 32

Slide 32 text

i. preparation 32

Slide 33

Slide 33 text

# lib/tasks/create_db.rake desc "Generate the gold master test database" task :create_db do destination = Rails.root.join('spec/fixtures/gold_master.sql') sh "pg_dump sanitized_utility_database #{destination}" end 33

Slide 34

Slide 34 text

ii. test 34

Slide 35

Slide 35 text

# spec/features/gold_master_spec.rb describe 'Fan.shred!' do it 'produces a consistent result' do end end 35

Slide 36

Slide 36 text

# spec/features/gold_master_spec.rb ApplicationRecord.connection.execute <<-SQL truncate schema_migrations; #{Rails.root.join('spec/fixtures/gold_master.sql').read} SQL 36

Slide 37

Slide 37 text

# spec/features/gold_master_spec.rb actual = Fan.shred! 37

Slide 38

Slide 38 text

testing strategy this test can do two things: 1.first run: generate gold master 2.subsequent runs: compare current result to gold master 38

Slide 39

Slide 39 text

# spec/features/gold_master_spec.rb gold_master_file = Rails.root.join('spec/fixtures/gold_master.txt') if !gold_master_file.exist? gold_master_file.write(actual) ... 39

Slide 40

Slide 40 text

# spec/features/gold_master_spec.rb gold_master_file = Rails.root.join('spec/fixtures/gold_master.txt') if !gold_master_file.exist? gold_master_file.write(actual) else gold_master = gold_master_file.read if gold_master != actual gold_master_file.write(actual) end ... 40

Slide 41

Slide 41 text

# spec/features/gold_master_spec.rb gold_master_file = Rails.root.join('spec/fixtures/gold_master.txt') if !gold_master_file.exist? gold_master_file.write(actual) else gold_master = gold_master_file.read if gold_master != actual gold_master_file.write(actual) end expect(actual).to eq(gold_master) end 41

Slide 42

Slide 42 text

# spec/features/gold_master_spec.rb describe 'Fan.shred!' do it 'produces a consistent result' do ApplicationRecord.connection.execute <<-SQL truncate schema_migrations; #{Rails.root.join('spec/fixtures/gold_master.sql').read} SQL actual = Fan.shred! gold_master_file = Rails.root.join('spec/fixtures/gold_master.txt') if !gold_master_file.exist? gold_master_file.write(actual) else gold_master = gold_master_file.read if gold_master != actual gold_master_file.write(actual) end expect(actual).to eq(gold_master) end end end 42

Slide 43

Slide 43 text

iii. evaluation 43

Slide 44

Slide 44 text

what happens when it fails? 44

Slide 45

Slide 45 text

3. working with the test 45

Slide 46

Slide 46 text

46

Slide 47

Slide 47 text

ideal application • [x] mature • [x] complex • [x] we expect minimal change to output 47

Slide 48

Slide 48 text

assertion The homepage, given the same data, should not change, without us knowing why. https://github.com/hashrocket/hr-til/tree/gold- master-demo 48

Slide 49

Slide 49 text

preparation revisited • get a production database dump • scrub sensitive information • dump as plaintext sql • check in sql 49

Slide 50

Slide 50 text

scrub data -- db/sanitize_production.sql -- Sanitize devs update developers set username = concat('rocketeer', id), email = concat('rocketeer', id, '@hashrocket.com'), twitter_handle = concat('hashrocket', id), admin = false, slack_name = concat('rocketeer', id); -- Clear sessions delete from authem_sessions; -- Limit the posts delete from posts where id > 200; 50

Slide 51

Slide 51 text

# spec/features/gold_master_spec.rb describe 'gold master' do before do ApplicationRecord.connection.execute <<-SQL truncate schema_migrations; #{Rails.root.join('spec/fixtures/gold_master.sql').read} SQL end end 51

Slide 52

Slide 52 text

# spec/features/gold_master_spec.rb visit root_path 52

Slide 53

Slide 53 text

# spec/features/gold_master_spec.rb page_html = page.html 53

Slide 54

Slide 54 text

# spec/features/gold_master_spec.rb describe 'gold master' do before do ApplicationRecord.connection.execute <<-SQL truncate schema_migrations; #{Rails.root.join('spec/fixtures/gold_master.sql').read} SQL visit root_path end specify 'the homepage does not change' do page_html = page.html gold_master_file = Rails.root.join('spec/fixtures/gold_master.txt') if !gold_master_file.exist? gold_master_file.write(page_html) else gold_master = gold_master_file.read if gold_master != page_html gold_master_file.write(page_html) end expect(page_html).to eq(gold_master) end end end 54

Slide 55

Slide 55 text

55

Slide 56

Slide 56 text

56

Slide 57

Slide 57 text

challenges • maintenance -> it's an investment • slower -> we can optimize data • implies correctness -> teaching opportunity 57

Slide 58

Slide 58 text

future plans 58

Slide 59

Slide 59 text

59

Slide 60

Slide 60 text

conclusion 60

Slide 61

Slide 61 text

thank you! jake @jwworth worth hashrocket 61

Slide 62

Slide 62 text

further reading • Slides: https://speakerdeck.com/jwworth • Code samples: https://github.com/hashrocket/hr-til/tree/gold-master-demo • Image Source: Watermelon Kaboom!!, KT King, Flickr.com, https://www.flickr.com/photos/xtrah/5005443977, Accessed 18 Mar 2017. 1. Change Code Without Fear, Nada daVeiga, Dr. Dobb's, http://www.drdobbs.com/tools/change-code-without-fear/206105233, February 6, 2008. Accessed 26 February, 2017. 2. Detecting behavioural changes when refactoring a web-based legacy system, Peter Spegel, May 21, 2015. 3. Gold Master Testing, Bryan Helmkamp, Code Climate Blog, http://blog.codeclimate.com/blog/2014/02/20/gold-master-testing/, February 20, 2014. Accessed February 26, 2017. 4. Gold Master Testing, Jake Worth, Hashrocket Blog, https://hashrocket.com/blog/posts/gold-master-testing, November 8, 2016. 5. How I run Legacy Code Retreat, J. B. Rainsberger, Legacy Code Retreat, http://legacycoderetreat.typepad.com/blog/2011/11/how-i-run-legacy-code- retreat.html, November 21, 2011. Accessed 26 February, 2017. 6. How Not To Write Golden Master Tests, J. B. Rainsberger, The Code Whisperer, http://blog.thecodewhisperer.com/permalink/how-not-to-write-golden-master- tests. Accessed 22 March, 2017. 7. Using the Golden Master technique to test legacy code, Chris Melinn, https://chrismelinn.wordpress.com/2013/04/12/using-the-golden-master-technique-to- test-legacy-code/, 12 April 2013. Accessed 26 February, 2017. 8. Working Effectively With Characterization Tests - Part 2, Alberto Savoia, http://www.artima.com/weblogs/viewpost.jsp?thread=198674, March 13, 2007. Accessed February 26, 2017. 9. Working Effectively With Characterization Tests, Alberto Savoia, artima developer, http://www.artima.com/weblogs/viewpost.jsp?thread=198296, March 9, 2007. Accessed February 26, 2017. 10.Working Effectively With Legacy Code, Michael C. Feathers, 2005. 62