Test Driven Development SN Biggs York Plasma Institute

TDD|7th July 2017|1/40 Introduction

Image ref: https://usercontent2.hubstatic.com/8530569 f520.jpg TDD|7th July 2017|2/40 Outline

Unit Testing is a Safety Net Unit Test Structure: Setup, Exercise, Assert xUnit – The Testing Framework Exercise – Write Some Unit Tests Test Driven Development (TDD) A Different Mindset Test First!? Fail, Pass, Refactor: A Simple Example Exercise – Take TDD for a Test Drive A Word of Warning: Pick the Right Tool for the Right Job Summary and Further Reading

TDD|7th July 2017|3/40 Unit Testing is a Safety Net

Image ref: http://www.fitnessbin.com/wp-content/uploads/2015/12/Rock-Climbing-4.jpg TDD|7th July 2017|4/40 Unit Testing is a Safety Net

Image ref: http://www.jamkey.fr/wp-content/uploads/2015/05/tests-pyramid.png TDD|7th July 2017|5/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Unit Testing is a Safety Net

TDD|7th July 2017|6/40 Test Structure: Setup, Exercise, Assert

class Calculator():

def add(self, augend, addend): 'Adds the addend to the augend' return augend+ addend

TDD|7th July 2017|7/40 Test Structure: Setup, Exercise, Assert

def testAddBothPositive(self): # Setup a=3 b=2 calc= Calculator() # Specify expected result expected=5 # Exercise system under test actual= calc.add(a, b) # Verify result self.assertEqual(actual, expected)

TDD|7th July 2017|8/40 xUnit - The Testing Framework

Began as sUnit for the language by in 1998 Since ported to many languages (cUnit, cppUnit, fUnit, pyUnit, etc.) Object-oriented - test classes inherit assertions Provides test suites, test runners, formatted test results, etc.

Image ref: https://d21ii91i3y6o6h.cloudfront.net/gallery images/from proof/4404/large/1425068890/xunit-dot-net-small-logo.png

TDD|7th July 2017|9/40 xUnit - The Testing Framework

import unittest from Calculator import Calculator

class testCalculator(unittest.TestCase):

def setUp(self): self.calc= Calculator()

def testAddBothPositive(self): 'Tests that two positive numbers are added correctly' # Exercise system under test and verify result self.assertEqual(self.calc.add(3,2),5)

TDD|7th July 2017|10/40 xUnit - The Testing Framework

To run the unit tests (from bash):

$ python -m unittest module[.class[.test]] [-v] e.g.

$ python -m unittest testCalculator.testCalculator.testAddBothPositive or discover tests automatically:

$ python -m unittest discover

TDD|7th July 2017|11/40 Exercise - Write Some Unit Tests

∗ 1 Download my basic calculator class from here: http://www- users.york.ac.uk/∼snb519/coding-club-tdd-examples/Calculator.py 2 Write a test class, testCalculator.py, that extends unittest.TestCase (see slide 10) 3 Write tests (see slide 8) for adding different combinations of numbers, e.g. both positive, both negative, one of each, floats, etc. 4 Run the tests (see slide 11) to see what the test results look like – try with and without the -v flag, and try running individual tests and test discovery 5 Try changing Calculator.py to see what failing tests look like 6 Extension - can you write any tests that should pass but actually fail, e.g. test that passing in strings is handled correctly

∗You can also get the slides here: http://www-users.york.ac.uk/∼snb519/coding-club-tdd-examples/slides.pdf TDD|7th July 2017|12/40 A Different Mindset

Image ref: http://www.fitnessbin.com/wp-content/uploads/2015/12/Rock-Climbing-4.jpg TDD|7th July 2017|13/40 Test First!?

Image ref: https://leantesting-wp.s3.amazonaws.com/resources/wp-content/uploads/2015/02/tdd-circle-of-life.png TDD|7th July 2017|14/40 Test First!?

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|15/40 Test First!?

Image ref: https://leantesting-wp.s3.amazonaws.com/resources/wp-content/uploads/2015/02/tdd-circle-of-life.png TDD|7th July 2017|16/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|17/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|18/40 Fail, Pass, Refactor: A Simple Example

def testSubtractBothPositive(self): 'Tests that two positive numbers are subtracted correctly' # Exercise system under test and verify result self.assertEqual(self.calc.subtract(3,2),1)

TDD|7th July 2017|19/40 Fail, Pass, Refactor: A Simple Example

E ======ERROR: testSubtractBothPositive (testCalculator.testCalculator) Tests that two positive numbers are subtracted correctly ------Traceback (most recent call last): File "testCalculator.py", line 40, in testSubtractBothPositiv self.assertEqual(self.calc.subtract(3, 2), 1) AttributeError: Calculator instance has no attribute 'subtract'

------Ran 1 test in 0.000s

FAILED (errors=1)

TDD|7th July 2017|20/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|21/40 Fail, Pass, Refactor: A Simple Example

def subtract(self): pass

TDD|7th July 2017|22/40 Fail, Pass, Refactor: A Simple Example

E ======ERROR: testSubtractBothPositive (testCalculator.testCalculator) Tests that two positive numbers are subtracted correctly ------Traceback (most recent call last): File "testCalculator.py", line 40, in testSubtractBothPositiv self.assertEqual(self.calc.subtract(3, 2), 1) TypeError: subtract() takes exactly 1 argument (3 given)

------Ran 1 test in 0.000s

FAILED (errors=1)

TDD|7th July 2017|23/40 Fail, Pass, Refactor: A Simple Example

def subtract(self, minuend, subtrahend): pass

TDD|7th July 2017|24/40 Fail, Pass, Refactor: A Simple Example

F ======FAIL: testSubtractBothPositive (testCalculator.testCalculator) Tests that two positive numbers are subtracted correctly ------Traceback (most recent call last): File "testCalculator.py", line 40, in testSubtractBothPositiv self.assertEqual(self.calc.subtract(3, 2), 1) AssertionError: None != 1

------Ran 1 test in 0.000s

FAILED (failures=1)

TDD|7th July 2017|25/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|26/40 Fail, Pass, Refactor: A Simple Example

def subtract(self, minuend, subtrahend): return1

TDD|7th July 2017|27/40 Fail, Pass, Refactor: A Simple Example

. ------Ran 1 test in 0.000s

OK

TDD|7th July 2017|28/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|29/40 Fail, Pass, Refactor: A Simple Example

def subtract(self, minuend, subtrahend): return3-2

TDD|7th July 2017|30/40 Fail, Pass, Refactor: A Simple Example

. ------Ran 1 test in 0.000s

OK

TDD|7th July 2017|31/40 Fail, Pass, Refactor: A Simple Example

def subtract(self, minuend, subtrahend): return minuend-2

TDD|7th July 2017|32/40 Fail, Pass, Refactor: A Simple Example

. ------Ran 1 test in 0.000s

OK

TDD|7th July 2017|33/40 Fail, Pass, Refactor: A Simple Example

def subtract(self, minuend, subtrahend): return minuend- subtrahend

TDD|7th July 2017|34/40 Fail, Pass, Refactor: A Simple Example

. ------Ran 1 test in 0.000s

OK

TDD|7th July 2017|35/40 Fail, Pass, Refactor: A Simple Example

1 Identify desired functionality 2 Write failing test 3 Make it compile as quickly as possible 4 Make it pass a quickly as possible 5 Remove duplication while maintaining 100% pass rate 6 Repeat as required

TDD|7th July 2017|36/40 Exercise - Write Some Unit Tests

1 Using Calculator.py and testCalculator.py that you have already developed, test and implement calc.multiply(multiplicand, multiplier) and calc.divide(divend, divisor) using TDD 2 Implement some extra features using TDD, e.g. handling two numbers passed in as strings element-wise array operations (see numpy.testing.assert array equals) raising exceptions for invalid inputs (see unittest.TestCase.assertRaises) printing to screen when an optional argument is given (redirect sys.stdout) calculations on a custom class yet to be defined (pass in a mock subclass)

TDD|7th July 2017|37/40 A Word of Warning Pick the right tool for the right job!

TDD|7th July 2017|38/40 Summary

Unit Testing Unit Testing is a Safety Net Unit Test Structure: Setup, Exercise, Assert xUnit is The Testing Framework Test Driven Development (TDD) A Different Mindset Test First! Fail, Pass, Refactor Pick the Right Tool for the Right Job

TDD|7th July 2017|39/40 Further Reading

Beck, K 2003 “Test-driven Development: By Example” Addison-Wesley, Kent Beck signature book. Feathers, M 2004 “Working Effectively with Legacy Code” Pearson Education, Robert C. Martin Series. Martin, RC 2008 “Clean Code: A Handbook of Agile Software Craftsmanship” Pearson Education. Hunt, A and Thomas, D 1999 “The Pragmatic Programmer: From Journeyman to Master” Pearson Education.

Image ref: http://d.gr-assets.com/books/1372039943l/387190.jpg

TDD|7th July 2017|40/40