Presentation is loading. Please wait.

Presentation is loading. Please wait.

Unit Testing.

Similar presentations


Presentation on theme: "Unit Testing."— Presentation transcript:

1 Unit Testing

2 Today's Goal Functions are the basic building block of programs.
Today, you'll learn to test functions, and how to design testable functions.

3 Testing Functions Amy is writing a program. She adds functionality by defining a new function, and adding a function call to the new function in the main program somewhere. Now she needs to test the new code. Two basic approaches to testing the new code: Run the entire program The function is tested when it is invoked during the program execution Test the function by itself, somehow Which do you think will be more efficient?

4 Manual Unit Test A unit test is a program that tests a function to determine if it works properly A manual unit test Gets test input from the user Invokes the function on the test input Displays the result of the function How would you use a unit test to determine that roundIt() works correctly? How does the test - debug cycle go? def roundIt(num: float) -> int: return int(num + .5) # ---- manual unit test ---- x = float(input('Enter a number:')) result = roundIt(x) print('Result: ', result)

5 Automated Unit Test An automated unit test Advantages? Disadvantages?
Invokes the function on predetermined test input Checks that the function produced the expected result Displays a pass / fail message Advantages? Disadvantages? def roundIt(num: float) -> int: return int(num + .5) # ---- automated unit test ---- result = roundIt(9.7) if result == 10: print("Test 1: PASS") else: print("Test 1: FAIL") result = roundIt(8.2) if result == 8: print("Test 2: PASS") print("Test 2: FAIL")

6 Automated Unit Test with assert
The assert statement Performs a comparison If the comparison is True, does nothing If the comparison is False, displays an error and stops the program assert statements help us write concise, automated unit tests Demo: Run the test def roundIt(num: float) -> int: return int(num + .5) # ---- automated unit test ---- result = roundIt(9.7) assert result == 10 result = roundIt(8.2) assert result == 8 print("All tests passed!")

7 A Shorter Test This is Nice. Two issues:
To test the function, we have to copy it between the "real program" and this unit test Assertion failure messages could be more helpful To solve these, we'll use pytest, a unit test framework def roundIt(num: float) -> int: return int(num + .5) # ---- automated unit test ---- assert roundIt(9.7) == 10 assert roundIt(8.2) == 8 print("All tests passed!")

8 A pytest Unit Test To create a pytest Unit Test:
Define a unit test function named test_something Place one or more assertions inside These tests can be located in the same file as the "real program," as long as you put the real program inside a special if statement, as shown To run a pytest Unit Test from command prompt: pytest program.py Note: pytest must first be installed using: pip install pytest def roundIt(num: float) -> int: return int(num + .5) def test_roundIt(): assert roundIt(9.7) == 10 assert roundIt(8.2) == 8 if __name__ == "__main__": # --- "real program" here ---

9 What's with this if statement?
if __name__ == "__main__": # --- "real program" here --- This if condition above is true when the program is executed using the python command: python myprog.py The if condition is false when the program is executed using pytest pytest myprog.py We don't want pytest to execute the main program...

10 pytest Unit Tests A pytest Unit Test can have more than one test method It's good practice to have a separate test method for each type of test That way, when a test fails, you can debug the issue more easily def roundIt(num: float) -> int: return int(num + .5) # ---- automated unit test ---- def test_roundIt_rounds_up(): assert roundIt(9.7) == 10 def test_roundIt_rounds_down(): assert roundIt(8.2) == 8

11 Understanding pytest Test Failures
A buggy roundIt function Diagnose pytest failure: ===== FAILURES ====== def test_roundIt_rounds_up(): > assert roundIt(9.7) == 10 E assert 9 == 10 E where 9 = roundIt(9.7) def roundIt(num: float) -> int: return int(num + .1) def test_roundIt_rounds_up(): assert roundIt(9.7) == 10 Expected value Actual value

12 Designing Testable Functions
Some functions can't be tested with an automated unit test. A testable function Gets input from parameters Returns a result Does no input / output # This function is not testable def roundIt(num: float) -> int: print(int(num + .5))

13 Design By Contract

14 Function Specifications
In order to test a function, we must have a clear specification for it. The specification must state What parameter values are acceptable What the function produces

15 Design by Contract Design By Contract is an approach to specifying function behavior in terms of preconditions and postconditions. Preconditions state restrictions on legal parameter values Postconditions state what the function promises to do if the preconditions are met The preconditions and postconditions form a contract between the function and its clients def sumNums(lo: int, hi: int) -> int: sum = 0 for i in range(lo, hi+1): sum += i return sum "I, function sumNums, promise to compute the sum of the numbers [lo..hi] if you will give me integers lo <= hi."

16 Documenting Function Contract
We use a triple-quoted string at the beginning of a function to document its contract. def sumNums(lo: int, hi: int) -> int: """Computes sum of sequence Preconditions: `lo` <= `hi` Postconditions: returns sum of numbers in range [`lo` .. `hi`] """ sum = 0 for i in range(lo, hi+1): sum += i return sum

17 Specification Based Testing
The function contract helps us design the unit tests. Unit tests should respect preconditions. Unit tests should verify postconditions. Which of these are valid unit tests? assert sumNums(1, 3) == 6 assert sumNums(1, 1) == 1 assert sumNums(1, 0) == 0 assert sumNums(-2, -1) == -3 def sumNums(lo: int, hi: int) -> int: """Computes sum of sequence Preconditions: `lo` <= `hi` Postconditions: returns sum of numbers in range [`lo` .. `hi`] """ sum = 0 for i in range(lo, hi+1): sum += i return sum

18 Let's Practice Write the contract for getFirstWord
def getFirstWord(text: str) -> str: """Extracts first word from `text` Preconditions: ________________ Postconditions: _________________ """ spaceLoc = text.find(' ') if spaceLoc == -1: return text else: return text[:spaceLoc]

19 Let's Practice Write assertions for a unit test for getFirstWord.
def getFirstWord(text: str) -> str: """Extracts first word from `text` Preconditions: `text` contains one or more words separated by spaces Postconditions: returns first word in `text` """ spaceLoc = text.find(' ') if spaceLoc == -1: return text else: return text[:spaceLoc]

20 Test First Development

21 When Do We Write Tests? Traditionally, programmers worked like this:
Write a function Write a unit test for the function One day, someone decided to try it the other way around: Write the function That's the simple version, anyway... Which way is better?

22 Test First Development
Here's how it really works: Write the function interface and contract. Write a unit test. Run the test and watch it fail. Implement enough of the function to pass the unit test. Run the unit test. Debug until it passes. Repeat steps 2-4 until function is complete.

23 Let's Practice Begin by designing function interface and specification
def addNums(num: str) -> int: """Adds up all digits in `num` Preconditions: ____________________ Postconditions: ____________________ """ …

24 Let's Practice Write a simple return statement for function body
Write a test Run the test Implement the function def addNums(num: str) -> int: """Adds up all digits in `num` Preconditions: `num` contains only digits Postconditions: returns sum of digits in `num` """ return 0


Download ppt "Unit Testing."

Similar presentations


Ads by Google