TDD and the Art of the Minimal Tests
"Imagine you are in a room �lled with..."
Serious?
Nah, just casual?
Interested?
Getting introduced?
The beginning of thought is in disagreement - not only with others but also with ourselves. - Eric Ho�er
What is a test?
test /t ɛ st/ "an event or situation that reveals the strength or quality of someone or something by putting them under strain"
What is driven?
driven / ˈ dr ɪ vn/ motivated or determined by a speci�ed factor or feeling
What is development?
Lynoure Braakman
Software engineer
https://www.neuland-b�.de
twitter: @Lynoure #ETC2018
https://lynoure.net
lean & agile
eXtreme Programming (XP)
Test Driven Development
It's not
writing �rst all the tests
(adapted with a permission from turnoff.us)
"Start a painting with fresh ideas, and then let the painting replace your ideas with its ideas." - Darby Bannard
Test Driven Thinking
Red
smallest
test: class TaxIdValidation (TestCase): def test_valid_tax_id (self): tax_id = '24750815087' self.assertTrue(validate_tax_id(tax_id), msg='Test tax id should validate'
smaller
test: class TaxIdValidation (TestCase): def test_valid_tax_id (self): validate_tax_id()
"FAILED (errors=1)"
❤ FAIL
"NameError: name 'validate_iban' is not de�ned"
❤ FAIL
Refreshing failures roll o� the travel easel like ants from a picnic blanket. -Sara Genn
"OK"
That is NOT OK
Green
small
smaller
code: def validate_tax_id (): pass
"OK"
test: class TaxIdValidation (TestCase): def test_valid_tax_id (self): tax_id = '24750815087' self.assertTrue(validate_tax_id(tax_id), msg='Test tax id should validate'
test: class TaxIdValidation (TestCase): def test_valid_tax_id (self): tax_id = '24750815087' self.assertTrue(validate_tax_id(tax_id), msg='Test tax id should validate' code: def test_valid_tax_id (tax_id): if tax_id is '24750815087': return True
YAGNI
YAGNI You Ain't Gonna Need It
"As a software developer, you are your own worst enemy. The sooner you realize that, the better o� you’ll be.” -Je� Atwood
"Many are stubborn in pursuit of the path they have chosen, few in pursuit of the goal." -Friedrich Nietzsche
test: class TaxIdValidation (TestCase): def test_invalid_tax_id (self): tax_id = 'my tax id' self.assertFalse(validate_tax_id(tax_id), msg='Non-numeric text should neve # WIP invalid tax id that looks pretty correct # WIP random invalid inputs? def test_valid_tax_id (self): tax_id = '24750815087' self.assertTrue(validate_tax_id(tax_id), msg='Test tax id should validate'
test: class TaxIdValidation (TestCase): def test_invalid_tax_id (self): tax_id = 'my tax id' self.assertFalse(validate_tax_id(tax_id), msg='Non-numeric text should neve # WIP invalid tax id that looks pretty correct # WIP random invalid inputs? def test_valid_tax_id (self): tax_id = '24750815087' self.assertTrue(validate_tax_id(tax_id), msg='Test tax id should validate' code: def test_valid_tax_id (tax_id): if tax_id is '24750815087': #YAGNI? return True else : return False
❤ FAIL
❤ FAIL ❤ OK
photo CC BY 2.0 Alper Çu ğ un
Refactor
Refactor your code
Refactoring or implementing?
Pause to think
One co�ee with a failing test, please!
Trust your discomfort
More data
test: class TaxIdValidation (TestCase): ... def test_valid_tax_id (self): self.assertTrue(validate_tax_id('24750815087'), msg='Original test tax id
test: class TaxIdValidation (TestCase): ... def test_valid_tax_id (self): self.assertTrue(validate_tax_id('24750815087'), msg='Original test tax id code: def _tax_id_checksum (value): product = 10 for digit in value: total = (int(digit) + product) % 10 product = (total * 2) % 11 checksum = 11 - product if checksum is 10: checksum = 0 return checksum def validate_tax_id (value): value = (value.replace(' ', '')).strip() if not re.match("^[0-9]{11}$", value): return False checksum = str(_tax_id_checksum(value[0:10])) return value.endswith(checksum)
Almost done!
test: class TaxIdValidation (TestCase): ... def test_valid_tax_id (self): self.assertTrue(validate_tax_id('24750815087'), msg='Original test tax id self.assertTrue(validate_tax_id('12345678903'), msg='Second test tax id sho
test: class TaxIdValidation (TestCase): ... def test_valid_tax_id (self): self.assertTrue(validate_tax_id('24750815087'), msg='Original test tax id self.assertTrue(validate_tax_id('12345678903'), msg='Second test tax id sho code: def _tax_id_checksum (value): product = 10 for digit in value: total = (int(digit) + product) % 10 product = (total * 2) % 11 checksum = 11 - product if checksum is 10: checksum = 0 return checksum def validate_tax_id (value): value = (value.replace(' ', '')).strip() if not re.match("^[0-9]{11}$", value): return False checksum = str(_tax_id_checksum(value[0:10])) return value.endswith(checksum)
"FAILED (failures=1)"
code: def _tax_id_checksum (value): product = 10 for digit in value: total = (int(digit) + product) % 10 if total is 0: # This was missing total = 10 product = (total * 2) % 11 checksum = 11 - product if checksum is 10: checksum = 0 return checksum def validate_tax_id (value): value = (value.replace(' ', '')).strip() if not re.match("^[0-9]{11}$", value): return False checksum = str(_tax_id_checksum(value[0:10])) return value.endswith(checksum)
The plural of 'test input' is not 'test data'
"What would you recommend I use for test data?"
Giving back to the testers
Refactor the tests as well
What kind of tests?
Test pyramids
(CC BY 2.0 Jerome Bon)
(CC BY 2.0 Sheila Sund)
(CC BY-SA 2.0 John Morton)
(by Staffan Andersson)
Good tests alert you to take action, and make it easy to �gure out what kind of action to take
TDD alone and with others
Alone
Whole team does TDD
No one does TDD
First in the team doing TDD
First in the team doing TDD
First in the team doing TDD
A pair of programmers is happier than one
If you CI something, it says something
Simple recipe for more and better TDD: 1. Come up with a tiny test for it
Simple recipe for more and better TDD: 1. Come up with a tiny test for it 2. Take the smallest action that can take you there
Simple recipe for more and better TDD: 1. Come up with a tiny test for it 2. Take the smallest action that can take you there 3. Once you got there, re�ect and adjust
Simple recipe for more and better TDD: 1. Come up with a tiny test for it 2. Take the smallest action that can take you there 3. Once you got there, re�ect and adjust 4. Repeat
The End 2018 CC BY-SA 2.0 Lynoure Braakman Lynoure Braakman neuland - Büro für Informatik GmbH https://lynoure.net https://www.neuland-bfi.de @Lynoure #ETC2018
Recommend
More recommend