software testing lecture 2 introduction to unit testing
play

Software Testing Lecture 2 Introduction to Unit Testing and TDD - PowerPoint PPT Presentation

Software Testing Lecture 2 Introduction to Unit Testing and TDD Justin Pearson 2019 1 / 91 Unit Testing xUnit testing is a framework where individual functions and methods are tested. It is not particularly well suited to integration


  1. Software Testing Lecture 2 Introduction to Unit Testing and TDD Justin Pearson 2019 1 / 91

  2. Unit Testing ◮ xUnit testing is a framework where individual functions and methods are tested. ◮ It is not particularly well suited to integration testing or regression testing. ◮ The best way to write testable code is to write the tests as you develop the code. ◮ Writing the test cases after the code takes more time and effort than writing the test during the code. It is like good documentation; you’ll always find something else to do if you leave until after you’ve written the code. 2 / 91

  3. The heart of Unit Testing The unit test framework is quite powerful. But the heart are two functions: ◮ assertTrue ◮ assertFalse 3 / 91

  4. assertTrue Suppose we want to test our string length function int Then, the following things should be true: istrlen(char*) ◮ The length of "Hello" is 5. ◮ The length of "" is 0. ◮ The length of "My kingdom for a horse." is 23. 4 / 91

  5. assertTrue Then we would assert that the following things are true: ◮ assertTrue (The length of "Hello" is 5.) ◮ assertTrue (The length of "" is 0.) ◮ assertTrue (The length of "My kingdom for a horse." is 23.) 5 / 91

  6. assertTrue ◮ Key idea in xUnit: ◮ assertTrue ( executable code ) ◮ Runs the executable code which should evaluate to true. ◮ assertFalse ( executable code) ◮ Runs the executable code which should evaluate to false. 6 / 91

  7. ◮ Different xUnit frameworks run the tests in different ways. ◮ Python unit testing framework has a notion of test suites and registries. ◮ But it is quite simple to set up tests. ◮ Key to success in understanding complex APIs. Take example code and modify it to do what you want. 7 / 91

  8. Python Unit Testing import c o d e t o b e t e s t e d u n i t t e s t import class TestCode ( u n i t t e s t . TestCase ) : t e s t s ( s e l f ) : def x = "Hello" s e l f . assertTrue ( len ( x ) == 5) 8 / 91

  9. Important Unit Testing Concepts ◮ Setup. You might need to initialise some data structures. 9 / 91

  10. Important Unit Testing Concepts ◮ Setup. You might need to initialise some data structures. ◮ The tests. Well, you need to do tests. 10 / 91

  11. Important Unit Testing Concepts ◮ Setup. You might need to initialise some data structures. ◮ The tests. Well, you need to do tests. ◮ Teardown. Always! clean up after you. 11 / 91

  12. Important Unit Testing Concepts ◮ Setup. You might need to initialise some data structures. ◮ The tests. Well, you need to do tests. ◮ Teardown. Always! clean up after you. Important idea. ◮ Each test should be able to be run independently of the other tests. You don’t know in what order the tests will be run. Or even if all tests will be run. The programmer might just rerun the test that caused problems. 12 / 91

  13. Teardown ◮ Teardown is more common in languages without automatic garbage collection. 13 / 91

  14. Test Driven Development ◮ Test driven development (TDD) is a way of programming where all your development is driven by tests. ◮ Write tests before you write code. ◮ Only write code when you fail a test. 14 / 91

  15. The TDD Mantra ◮ Red: Write a test that fails. ◮ Green: Write code that passes the test. ◮ Refactor: If possible refactor your code. It seems a bit counter intuitive at first. I must think about the algorithm, not the tests. Can I write complex algorithms this way? 15 / 91

  16. TDD ◮ The key is to start small. Simple tests. ◮ Grow your code slowly. ◮ Only make your code more complex if the tests require it. ◮ Think of a good sequence of tests. 16 / 91

  17. Two Examples ◮ Converting numbers to English. ◮ A score board for the game of darts. 17 / 91

  18. Refactoring ◮ For us, refactoring will be taking a piece of code and making it simpler to understand and smaller. Hopefully it makes it more efficient. Although we won’t worry too much about efficiency. Clear, readable code is our goal. Efficiency comes when it is needed. ◮ “We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.” Donald Knuth. ◮ When you start doing OO design, then refactoring also means tinkering with the object hierarchy. 18 / 91

  19. toEnglish ◮ The idea is to write a function that given a number between 0 and 999 inclusive returns a string spelling out the number in English. For example toEnglish(43) should produce the string ’forty three’ . ◮ We are going to develop the function in Python. If you are not familiar with Python, then pretend it is pseudo code. ◮ See the web page for accompanying pdf file that explains how to set up the tests. 19 / 91

  20. Red First let us write a test that will fail. a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (0) , ’zero’ ) Apart from setting up the minimal amount of files to get the modules going we have no code. This test will fail. 20 / 91

  21. Green ◮ Do not think ahead. Write the minimal code that will pass the test. t o E n g l i s h (n ) : def return ( ’zero’ ) Stupid? Well it gets us going. 21 / 91

  22. Red We need to find a test that fails. a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (1) , ’one’ ) This test of course fails. self.assertEqual(toEnglish.toEnglish(1),’one’) AssertionError: ’zero’ != ’one’ 22 / 91

  23. Green Write the minimum to pass the test. def t o E n g l i s h (n ) : n==0: i f return ( ’zero’ ) n==1: e l i f return ( ’one’ ) Now all the tests pass. elif is short for else if 23 / 91

  24. Red You can see where this is going. a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (2) , ’two’ ) 24 / 91

  25. Green t o E n g l i s h (n ) : def i f n==0: return ( ’zero’ ) e l i f n==1: return ( ’one’ ) e l i f n==2: return ( ’two’ ) 25 / 91

  26. We are on a roll. We understand our tests and understand our code. This will not go on for ever, but it is a start. a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (3) , ’three’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (4) , ’four’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (5) , ’five’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (6) , ’six’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (7) , ’seven’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (8) , ’eight’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (9) , ’nine’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (10) , ’ten’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (11) , ’eleven’ ) 26 / 91

  27. You can guess what the code looks like. t o E n g l i s h (n ) : def i f n==0: return ( ’zero’ ) e l i f n==1: return ( ’one’ ) e l i f n==2: return ( ’two’ ) n==3: e l i f return ( ’three’ ) n==4: e l i f return ( ’four’ ) n==5: e l i f . . . . . . . 27 / 91

  28. Refactor Now we are being too slow and stupid. Refactor. Use a list instead of a bunch of if statements. t o E n g l i s h (n ) : def numbers = [ ’zero’ , ’one’ , ’two’ , ’three’ , ’four’ , ’five’ , ’six’ , ’seven’ , ’eight’ , ’nine’ , ’ten’ , ’eleven’ ] return ( numbers [ n ] ) Rerun the tests to make sure that your refactoring does not mess anything up. 28 / 91

  29. Red We need to find tests that fail. While we know what is going on we take bigger steps. a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (12) , ’twelve’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (13) , ’thirteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (14) , ’fourteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (15) , ’fifteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (16) , ’sixteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (17) , ’seventeen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (18) , ’eighteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (19) , ’nineteen’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (20) , ’twenty’ ) a s s e r t E q u a l ( t o E n g l i s h . t o E n g l i s h (21) , ’twenty�one’ ) 29 / 91

  30. Green Well the code should be no surprise. def t o E n g l i s h (n ) : numbers = [ ’zero’ , ’one’ , ’two’ , ’three’ , ’four’ , ’five’ , ’six’ , ’seven’ , ’eight’ , ’nine’ , ’ten’ , ’eleven’ , ’twelve’ , ’thirteen’ , ’fourteen’ , ’fifteen’ , ’sixteen’ , ’seventeen’ , ’eighteen’ , ’nineteen’ , ’twenty’ , ’twenty�one’ ] return ( numbers [ n ] ) 30 / 91

  31. Refactor Time to refactor ’twenty one’ is ’twenty’ + ’ ’ + ’one’ . This gives us the following code: def t o E n g l i s h (n ) : numbers = [ ’zero’ , ’one’ , ’two’ , ’three’ , ’four’ , ’five’ , ’six’ , ’seven’ , ’eight’ , ’nine’ , ’ten’ , ’eleven’ , ’twelve’ , ’thirteen’ , ’fourteen’ , ’fifteen’ , ’sixteen’ , ’seventeen’ , ’eighteen’ , ’nineteen’ , ’twenty’ ] i f n in range ( 0 , 2 0 ) : return ( numbers [ n ] ) else : return ( ’twenty’ + ’�’ + numbers [ n − 20]) 31 / 91

Recommend


More recommend