advanced developer testing

129
Three Advanced Developer Testing Techniques Alistair McKinnell @amckinnell

Upload: alistair-mckinnell

Post on 28-Jun-2015

226 views

Category:

Software


0 download

DESCRIPTION

Developers have unique opportunities to influence software quality. We explore three advanced testing techniques that belong in every developers toolbox: (1) characterization, (2) approval, and (3) mutation testing.

TRANSCRIPT

Page 1: Advanced Developer Testing

Three AdvancedDeveloper Testing Techniques

Alistair McKinnell@amckinnell

Page 2: Advanced Developer Testing

How do we change code safely?

Page 3: Advanced Developer Testing
Page 4: Advanced Developer Testing

Changing Code

Existing Behaviour New Behaviour

Page 5: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 6: Advanced Developer Testing

Gilded Rose

Page 7: Advanced Developer Testing

1 class GildedRose 2 attr_reader :items 3 4 def initialize(items) 5 @items = items 6 end 7 8 def update_quality 9 @items.each do |item| 10 if item.name != 'Aged Brie' and 11 item.name != 'Backstage passes to a TAFKAL80ETC concert' 12 if item.quality > 0 13 if item.name != 'Sulfuras, Hand of Ragnaros' 14 item.quality = item.quality - 1 15 end 16 end 17 else 18 if item.quality < 50 19 item.quality = item.quality + 1 20 if item.name == 'Backstage passes to a TAFKAL80ETC concert' 21 if item.sell_in < 11 22 if item.quality < 50 23 item.quality = item.quality + 1 24 end 25 end 26 if item.sell_in < 6 27 if item.quality < 50 28 item.quality = item.quality + 1 29 end 30 end 31 end 32 end 33 end 34 if item.name != 'Sulfuras, Hand of Ragnaros' 35 item.sell_in = item.sell_in - 1 36 end 37 if item.sell_in < 0 38 if item.name != 'Aged Brie' 39 if item.name != 'Backstage passes to a TAFKAL80ETC concert' 40 if item.quality > 0 41 if item.name != 'Sulfuras, Hand of Ragnaros' 42 item.quality = item.quality - 1 43 end 44 end 45 else 46 item.quality = item.quality - item.quality 47 end 48 else 49 if item.quality < 50 50 item.quality = item.quality + 1 51 end 52 end 53 end 54 end 55 end 56 57 end !

Page 8: Advanced Developer Testing

“Conjured” items degrade twice as fast as normal items.

New Behaviour

Page 9: Advanced Developer Testing

Existing Behaviour(Refactored)

New Behaviour(Conjured Item)

Changing Code

Page 10: Advanced Developer Testing

Are we ready to change code safely?

Page 11: Advanced Developer Testing

Nope

Refactoring safelyrequires tests.

Page 12: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 13: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

Page 14: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

expected: nil got: "Mail Armour, 9, 19"

Page 15: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

expected: nil got: "Mail Armour, 9, 19"

Page 16: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19') end

end

Page 17: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new(‘Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

Page 18: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

expected: nil got: "Mail Armour, 8, 18"

Page 19: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq(nil) end

end

expected: nil got: "Mail Armour, 8, 18"

Page 20: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [Item.new('Mail Armour', 10, 20)] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') end

end

Page 21: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new('Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') end

end

Page 22: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new('Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19')

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') end

end

Page 23: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new(‘Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19') expect(subject.items[1].to_s).to eq(nil)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') expect(subject.items[1].to_s).to eq(nil) end

end

Page 24: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new(‘Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19') expect(subject.items[1].to_s).to eq(nil)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') expect(subject.items[1].to_s).to eq(nil) end

end

Page 25: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new(‘Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19') expect(subject.items[1].to_s).to eq(‘Aged Brie, 3, 10')

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') expect(subject.items[1].to_s).to eq(‘Aged Brie, 2, 11') end

end

Page 26: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do items = [ Item.new('Mail Armour', 10, 20), Item.new(‘Aged Brie', 4, 9) ] subject = GildedRose.new(items)

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 9, 19') expect(subject.items[1].to_s).to eq(‘Aged Brie, 3, 10')

subject.update_quality expect(subject.items[0].to_s).to eq('Mail Armour, 8, 18') expect(subject.items[1].to_s).to eq(‘Aged Brie, 2, 11') end

end

Let’s organize our test

Page 27: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 28: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def items end

def characterize(subject, days) end

def expected end

Page 29: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9] ]

item_attributes.map { |args| Item.new(*args) } end

Page 30: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def characterize(subject, days) characterization = []

(1..days).each do subject.update_quality

subject.items.each { |item| characterization << item.to_s } end

characterization end

Page 31: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected [ 'Mail Armour, 9, 19', 'Aged Brie, 3, 10', 'Mail Armour, 8, 18', 'Aged Brie, 2, 11' ] end

Page 32: Advanced Developer Testing

Are we ready to change code safely?

Page 33: Advanced Developer Testing

Nope

Characterizationis incomplete.

Page 34: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 35: Advanced Developer Testing
Page 36: Advanced Developer Testing

53%

Page 37: Advanced Developer Testing

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

Page 38: Advanced Developer Testing

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9] ]

item_attributes.map { |args| Item.new(*args) } end

Page 39: Advanced Developer Testing

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9] ]

item_attributes.map { |args| Item.new(*args) } end

Page 40: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17] ]

item_attributes.map { |args| Item.new(*args) } end

Page 41: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 42: Advanced Developer Testing

def expected [ 'Mail Armour, 9, 19', 'Aged Brie, 3, 10', 'Backstage passes to a TAFKAL80ETC concert, 14, 18', 'Mail Armour, 8, 18', 'Aged Brie, 2, 11', 'Backstage passes to a TAFKAL80ETC concert, 13, 19', ] end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 43: Advanced Developer Testing

def expected [ 'Mail Armour, 9, 19', 'Aged Brie, 3, 10', 'Backstage passes to a TAFKAL80ETC concert, 14, 18', 'Mail Armour, 8, 18', 'Aged Brie, 2, 11', 'Backstage passes to a TAFKAL80ETC concert, 13, 19', ] end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 44: Advanced Developer Testing

60%

Page 45: Advanced Developer Testing

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

Page 46: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

Page 47: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

if item.name == 'Backstage passes to a TAFKAL80ETC concert' if item.sell_in < 11 if item.quality < 50 item.quality = item.quality + 1 end end if item.sell_in < 6 if item.quality < 50 item.quality = item.quality + 1 end end end

Page 48: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

Page 49: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

This is what we want to do

Page 50: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

def expected [ 'Mail Armour, 9, 19', 'Aged Brie, 3, 10', 'Backstage passes to a TAFKAL80ETC concert, 14, 18', 'Mail Armour, 8, 18', 'Aged Brie, 2, 11', 'Backstage passes to a TAFKAL80ETC concert, 13, 19’,

and many more expected results ] end

Page 51: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def characterize(subject, days) characterization = []

(1..days).each do subject.update_quality

characterization.concat(subject.items.map(&:to_s)) end

Digest::SHA2.hexdigest(characterization.join) end

Page 52: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def characterize(subject, days) characterization = []

(1..days).each do subject.update_quality

characterization.concat(subject.items.map(&:to_s)) end

Digest::SHA2.hexdigest(characterization.join) end

Page 53: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected [ 'Mail Armour, 9, 19', 'Aged Brie, 3, 10', 'Backstage passes to a TAFKAL80ETC concert, 14, 18', 'Mail Armour, 8, 18', 'Aged Brie, 2, 11', 'Backstage passes to a TAFKAL80ETC concert, 13, 19’, ] end

Page 54: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected nil end

Page 55: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected nil end

expected: nil got: “c869cb44cd36e1553d18 … d958277a43b1adc8e8e5”

Page 56: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected nil end

expected: nil got: “c869cb44cd36e1553d18 … d958277a43b1adc8e8e5”

Page 57: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def expected ‘c869cb44cd36e1553d18 … d958277a43b1adc8e8e5’ end

Page 58: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

def expected ‘c869cb44cd36e1553d18 … d958277a43b1adc8e8e5’ end

Page 59: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

expected: “c869cb44cd36e1553d18 … d958277a43b1adc8e8e5” got: “fb61e7fdfffcba653fec … 9ae0512470a974b1561e"

def expected ‘c869cb44cd36e1553d18 … d958277a43b1adc8e8e5’ end

Page 60: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

def expected ‘fb61e7fdfffcba653fec … 9ae0512470a974b1561e’ end

Page 61: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

Page 62: Advanced Developer Testing

100%

Page 63: Advanced Developer Testing

Are we ready to change code safely?

Page 64: Advanced Developer Testing

Nope

Characterizationis incomplete.

Page 65: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 66: Advanced Developer Testing

class GildedRose attr_reader :items

def initialize(items) @items = items end

def update_quality @items.each do |item|

if item.name != 'Sulfuras, Hand of Ragnaros' item.sell_in = item.sell_in - 1 end

end end end

Page 67: Advanced Developer Testing

class GildedRose attr_reader :items

def initialize(items) @items = items end

def update_quality @items.each do |item|

# if item.name != 'Sulfuras, Hand of Ragnaros' item.sell_in = item.sell_in - 1 # end

end end end

Page 68: Advanced Developer Testing

class GildedRose attr_reader :items

def initialize(items) @items = items end

def update_quality @items.each do |item|

# if item.name != 'Sulfuras, Hand of Ragnaros' item.sell_in = item.sell_in - 1 # end

end end end

Comment out two lines

Page 69: Advanced Developer Testing

Test still passes?

class GildedRose attr_reader :items

def initialize(items) @items = items end

def update_quality @items.each do |item|

# if item.name != 'Sulfuras, Hand of Ragnaros' item.sell_in = item.sell_in - 1 # end

end end end

Page 70: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 71: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 72: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 73: Advanced Developer Testing

Are we ready to change code safely?

Page 74: Advanced Developer Testing

Nope

Characterizationis incomplete.

Page 75: Advanced Developer Testing

github.com/mbj/mutant

Page 76: Advanced Developer Testing

Mutant configuration:

Subjects: 2Mutations: 476 Kills: 454 Alive: 22 Coverage: 95.38% Expected: 100.00%

Page 77: Advanced Developer Testing

- if (item.quality > 0) + if (item.quality > 1)

- if (item.quality < 50) + if (item.quality < 49)

- if (item.quality < 50) + if (item.quality < 51)

- if (item.quality < 50) + if true

- if (item.quality < 50) + if 50

- if (item.name == "Backstage passes to a TAFKAL80ETC concert") + if item.name.eql?("Backstage passes to a TAFKAL80ETC concert")

Page 78: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Aged Brie', 4, 9], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 79: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 80: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 81: Advanced Developer Testing

Subjects: 2 Mutations: 476 Kills: 454 Alive: 22 Coverage: 95.38% Expected: 100.00%

Before AfterSubjects: 2 Mutations: 476 Kills: 474 Alive: 4 Coverage: 99.16% Expected: 100.00%

Page 82: Advanced Developer Testing

Are we ready to change code safely?

Page 83: Advanced Developer Testing

Yep

Page 84: Advanced Developer Testing

New BehaviourExisting Behaviour

Changing Code

Page 85: Advanced Developer Testing
Page 86: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 87: Advanced Developer Testing

Refactoring

Page 88: Advanced Developer Testing

1 class GildedRose 2 attr_reader :items 3 4 def initialize(items) 5 @items = items 6 end 7 8 def update_quality 9 @items.each { |item| update_item_quality(item) } 10 end 11 12 private 13 14 def update_item_quality(item) 15 return if item.name == 'Sulfuras, Hand of Ragnaros' 16 17 perform_inventory_rollover(item) 18 perform_inventory_expiration(item) 19 end 20 21 def perform_inventory_rollover(item) 22 item.sell_in -= 1 23 24 case item.name 25 when 'Aged Brie' 26 increase_quality(item) 27 when 'Backstage passes to a TAFKAL80ETC concert' 28 increase_quality(item) 29 increase_quality(item) if item.sell_in < 10 30 increase_quality(item) if item.sell_in < 5 31 else 32 decrease_quality(item) 33 end 34 end 35 36 def perform_inventory_expiration(item) 37 return unless expired?(item) 38 39 case item.name 40 when 'Aged Brie' 41 increase_quality(item) 42 when 'Backstage passes to a TAFKAL80ETC concert' 43 writeoff(item) 44 else 45 decrease_quality(item) 46 end 47 end 48 49 def expired?(item) 50 item.sell_in < 0 51 end 52 53 def decrease_quality(item) 54 item.quality -= 1 if 0 < item.quality 55 end 56 57 def increase_quality(item) 58 item.quality += 1 if item.quality < 50 59 end 60 61 def writeoff(item) 62 item.quality = 0 63 end 64 65 end

Page 89: Advanced Developer Testing

“Conjured” items degrade twice as fast as normal items.

New Behaviour

Page 90: Advanced Developer Testing

def perform_inventory_rollover(item)

case item.name when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

def perform_inventory_expiration(item)

case item.name

when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

Page 91: Advanced Developer Testing

def perform_inventory_rollover(item)

case item.name when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

def perform_inventory_expiration(item)

case item.name

when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

Page 92: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80], [‘Conjured Mana', 13, 50] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 93: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80], [‘Conjured Mana', 13, 50] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 94: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

def expected ‘1768fa473f323772588a … 13eab4018717ea78ea0c’ end

Page 95: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 20)

expect(characterization).to eq(expected) end

end

expected: “1768fa473f323772588a … 13eab4018717ea78ea0c” got: “6cf1111d5865232381f1 … 9d00ff8e831395de5420"

def expected ‘1768fa473f323772588a … 13eab4018717ea78ea0c’ end

Page 96: Advanced Developer Testing

New BehaviourExisting Behaviour

Changing Code

Page 97: Advanced Developer Testing

Are we ready to change code safely?

Page 98: Advanced Developer Testing

Nope

Can’t distinguish existing and new behaviour.

Page 99: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 100: Advanced Developer Testing

Whatever the code does.

Existing Behaviour

Page 101: Advanced Developer Testing

“Conjured” items degradetwice as fast as normal items.

New Behaviour

Page 102: Advanced Developer Testing

github.com/kytrinyx/approvals

Page 103: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

def characterize(subject, days) characterization = []

(1..days).each_with_index do |day| subject.update_quality

characterization << "Day #{day} of #{days}" subject.items.each { |item| characterization << " #{item.to_s}" } end

characterization.join("\n") end

Page 104: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

def characterize(subject, days) characterization = []

(1..days).each_with_index do |day| subject.update_quality

characterization << "Day #{day} of #{days}" subject.items.each { |item| characterization << " #{item.to_s}" } end

characterization.join("\n") end

Page 105: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

def characterize(subject, days) characterization = []

(1..days).each_with_index do |day| subject.update_quality

characterization << "Day #{day} of #{days}" subject.items.each { |item| characterization << " #{item.to_s}" } end

characterization.join("\n") end

Page 106: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

Page 107: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

Approvals::ApprovalError: Approval Error: Approval file “spec/fixtures/approvals/gildedrose/ knows_how_to_update_quality_for_items.approved.txt"not found.

Page 108: Advanced Developer Testing

knows_how_to_update_quality_for_items.received.txt

1 Day 1 of 20 2 Mail Armour, 9, 19 3 Mail Armour, 9, 0 4 Aged Brie, 3, 10 5 Aged Brie, 0, 50 6 Backstage passes to a TAFKAL80ETC concert, 14, 18 7 Backstage passes to a TAFKAL80ETC concert, 4, 50 8 Sulfuras, Hand of Ragnaros, -1, 80 9 Day 2 of 20 10 Mail Armour, 8, 18

153 Day 20 of 20 154 Mail Armour, -10, 0 155 Mail Armour, -10, 0 156 Aged Brie, -16, 45 157 Aged Brie, -19, 50 158 Backstage passes to a TAFKAL80ETC concert, -5, 0 159 Backstage passes to a TAFKAL80ETC concert, -15, 0 160 Sulfuras, Hand of Ragnaros, -1, 80

Page 109: Advanced Developer Testing

knows_how_to_update_quality_for_items.approved.txt

1 Day 1 of 20 2 Mail Armour, 9, 19 3 Mail Armour, 9, 0 4 Aged Brie, 3, 10 5 Aged Brie, 0, 50 6 Backstage passes to a TAFKAL80ETC concert, 14, 18 7 Backstage passes to a TAFKAL80ETC concert, 4, 50 8 Sulfuras, Hand of Ragnaros, -1, 80 9 Day 2 of 20 10 Mail Armour, 8, 18

153 Day 20 of 20 154 Mail Armour, -10, 0 155 Mail Armour, -10, 0 156 Aged Brie, -16, 45 157 Aged Brie, -19, 50 158 Backstage passes to a TAFKAL80ETC concert, -5, 0 159 Backstage passes to a TAFKAL80ETC concert, -15, 0 160 Sulfuras, Hand of Ragnaros, -1, 80

Page 110: Advanced Developer Testing

knows_how_to_update_quality_for_items.approved.txt

1 Day 1 of 20 2 Mail Armour, 9, 19 3 Mail Armour, 9, 0 4 Aged Brie, 3, 10 5 Aged Brie, 0, 50 6 Backstage passes to a TAFKAL80ETC concert, 14, 18 7 Backstage passes to a TAFKAL80ETC concert, 4, 50 8 Sulfuras, Hand of Ragnaros, -1, 80 9 Day 2 of 20 10 Mail Armour, 8, 18

153 Day 20 of 20 154 Mail Armour, -10, 0 155 Mail Armour, -10, 0 156 Aged Brie, -16, 45 157 Aged Brie, -19, 50 158 Backstage passes to a TAFKAL80ETC concert, -5, 0 159 Backstage passes to a TAFKAL80ETC concert, -15, 0 160 Sulfuras, Hand of Ragnaros, -1, 80

Page 111: Advanced Developer Testing

New BehaviourExisting Behaviour

Changing Code

Page 112: Advanced Developer Testing

def perform_inventory_rollover(item)

case item.name when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

def perform_inventory_expiration(item)

case item.name

when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

Page 113: Advanced Developer Testing

def perform_inventory_rollover(item

case item.name when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

def perform_inventory_expiration(item)

case item.name

when 'Conjured Mana' decrease_quality(item) decrease_quality(item) end

end

Page 114: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80], [‘Conjured Mana', 13, 50] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 115: Advanced Developer Testing

def items item_attributes = [ ['Mail Armour', 10, 20], ['Mail Armour', 10, 1], ['Aged Brie', 4, 9], ['Aged Brie', 1, 49], ['Backstage passes to a TAFKAL80ETC concert', 15, 17], ['Backstage passes to a TAFKAL80ETC concert', 5, 49], ['Sulfuras, Hand of Ragnaros', -1, 80], [‘Conjured Mana', 13, 50] ]

item_attributes.map { |args| Item.new(*args) } end

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items) characterization = characterize(subject, 2)

expect(characterization).to eq(expected) end

end

Page 116: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

Page 117: Advanced Developer Testing

describe GildedRose do

it 'knows how to update quality for items' do subject = GildedRose.new(items)

verify { characterize(subject, 20) } end

end

Approvals::ApprovalError: Approval Error: Received file “spec/fixtures/approvals/gildedrose/ knows_how_to_update_quality_for_items.received.txt” does not match approved “spec/fixtures/approvals/gildedrose/ knows_how_to_update_quality_for_items.approved.txt".

Page 118: Advanced Developer Testing

knows_how_to_update_quality_for_items.received.txt

4 Aged Brie, 3, 10 5 Aged Brie, 0, 50 6 Backstage passes to a TAFKAL80ETC concert, 14, 18 7 Backstage passes to a TAFKAL80ETC concert, 4, 50 8 Sulfuras, Hand of Ragnaros, -1, 80 9 Conjured Mana, 12, 48 10 Day 2 of 20 11 Mail Armour, 8, 18 12 …

knows_how_to_update_quality_for_items.approved.txt

4 Aged Brie, 3, 10 5 Aged Brie, 0, 50 6 Backstage passes to a TAFKAL80ETC concert, 14, 18 7 Backstage passes to a TAFKAL80ETC concert, 4, 50 8 Sulfuras, Hand of Ragnaros, -1, 80 9 Day 2 of 20 10 Mail Armour, 8, 18 11 …

Page 119: Advanced Developer Testing

knows_how_to_update_quality_for_items.received.txt

8 Sulfuras, Hand of Ragnaros, -1, 80 9 Conjured Mana, 12, 48 10 Day 2 of 20 11 Mail Armour, 8, 18 12 Mail Armour, 8, 0 13 Aged Brie, 2, 11 14 Aged Brie, -1, 50 15 Backstage passes to a TAFKAL80ETC concert, 13, 19 16 Backstage passes to a TAFKAL80ETC concert, 3, 50 17 Sulfuras, Hand of Ragnaros, -1, 80 18 Conjured Mana, 11, 46 19 Day 3 of 20 20 Mail Armour, 7, 17 21 Mail Armour, 7, 0 22 Aged Brie, 1, 12 23 Aged Brie, -2, 50 24 Backstage passes to a TAFKAL80ETC concert, 12, 20 25 Backstage passes to a TAFKAL80ETC concert, 2, 50 26 Sulfuras, Hand of Ragnaros, -1, 80 27 Conjured Mana, 10, 44 28 Day 4 of 20 29 Mail Armour, 6, 16 30 …

Page 120: Advanced Developer Testing

knows_how_to_update_quality_for_items.approved.txt

8 Sulfuras, Hand of Ragnaros, -1, 80 9 Conjured Mana, 12, 48 10 Day 2 of 20 11 Mail Armour, 8, 18 12 Mail Armour, 8, 0 13 Aged Brie, 2, 11 14 Aged Brie, -1, 50 15 Backstage passes to a TAFKAL80ETC concert, 13, 19 16 Backstage passes to a TAFKAL80ETC concert, 3, 50 17 Sulfuras, Hand of Ragnaros, -1, 80 18 Conjured Mana, 11, 46 19 Day 3 of 20 20 Mail Armour, 7, 17 21 Mail Armour, 7, 0 22 Aged Brie, 1, 12 23 Aged Brie, -2, 50 24 Backstage passes to a TAFKAL80ETC concert, 12, 20 25 Backstage passes to a TAFKAL80ETC concert, 2, 50 26 Sulfuras, Hand of Ragnaros, -1, 80 27 Conjured Mana, 10, 44 28 Day 4 of 20 29 Mail Armour, 6, 16 30 …

Page 121: Advanced Developer Testing

knows_how_to_update_quality_for_items.approved.txt

8 Sulfuras, Hand of Ragnaros, -1, 80 9 Conjured Mana, 12, 48 10 Day 2 of 20 11 Mail Armour, 8, 18 12 Mail Armour, 8, 0 13 Aged Brie, 2, 11 14 Aged Brie, -1, 50 15 Backstage passes to a TAFKAL80ETC concert, 13, 19 16 Backstage passes to a TAFKAL80ETC concert, 3, 50 17 Sulfuras, Hand of Ragnaros, -1, 80 18 Conjured Mana, 11, 46 19 Day 3 of 20 20 Mail Armour, 7, 17 21 Mail Armour, 7, 0 22 Aged Brie, 1, 12 23 Aged Brie, -2, 50 24 Backstage passes to a TAFKAL80ETC concert, 12, 20 25 Backstage passes to a TAFKAL80ETC concert, 2, 50 26 Sulfuras, Hand of Ragnaros, -1, 80 27 Conjured Mana, 10, 44 28 Day 4 of 20 29 Mail Armour, 6, 16 30 …

Page 122: Advanced Developer Testing

We win.

We preservedexisting behaviour and added new behaviour.

Page 123: Advanced Developer Testing

CharacterizationTesting

ApprovalTesting

CodeCoverage

MutationTesting

Page 124: Advanced Developer Testing

Are we ready to change code safely?

Page 125: Advanced Developer Testing

Changing Code

Existing Behaviour New Behaviour

Page 126: Advanced Developer Testing

Changing Code

New Behaviour(Approval Tests)

Existing Behaviour(Characterization Tests)

Page 127: Advanced Developer Testing

Be amazing atchanging code safely.

Page 128: Advanced Developer Testing

github.com/amckinnell/developer-testing

Page 129: Advanced Developer Testing

Recommended Reading