Code Testing
Contents
Code Testing¶
smoke tests
unit tests
pytest
Clicker Question #1¶
Given the following code, which assert will fail?
def extend(input_arg):
output = input_arg.copy()
for element in input_arg:
output.append(element)
return output
# test here
extend([1,2,3,4])
[1, 2, 3, 4, 1, 2, 3, 4]
A)
assert isinstance(extend([1, 2]), list)
B)
assert extend([1, 2]) == [1, 2, 1, 2]
C)
assert extend((1, 2)) == (1, 2, 1, 2)
D)
assert extend(['a', 'b', 'c']) == ['a', 'b', 'c', 'a', 'b', 'c']
E)
assert extend([]) == []
Clicker Question - Asserts¶
# Check that extend returns a list
assert isinstance(extend([1, 2]), list)
# Check that an input list returns the expected result
assert extend([1, 2]) == [1, 2, 1, 2]
# Check if the function works on tuples
assert extend((1, 2)) == (1, 2, 1, 2)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-6-c510c0c20682> in <module>()
1 # Check if the function works on tuples
----> 2 assert extend((1, 2)) == (1, 2, 1, 2)
<ipython-input-1-a3ca5f93bd43> in extend(input_arg)
1 def extend(input_arg):
----> 2 output = input_arg.copy()
3 for element in input_arg:
4 output.append(element)
5 return output
AttributeError: 'tuple' object has no attribute 'copy'
# Check that a different input list (different lengths / contents) returns expected result
assert extend(['a', 'b', 'c']) == ['a', 'b', 'c', 'a', 'b', 'c']
# Check that an empty list executes, executing an empty list
assert extend([]) == []
Code Testing¶
How to Write Tests¶
Given a function or class you want to test:
You need to have an expectation for what it should do
Write out some example cases, with known answers
Use
assert
to check that your example cases do run as expectedCollect these examples into test functions, stored in test files
Why Write Tests¶
To ensure code does what it is supposed to
To have a system for checking things when you change things in the code
The Best (Laziest) Argument for Writing Tests¶
Whenever you write new code, you will find yourself using little snippets of code to check it.
Collect these snippets into a test function, and you get re-runnable tests for free.
Example Test Code¶
def add(num1, num2):
return num1 + num2
add(2.7, 1.2) == 3.9
False
import math
def test_add():
"""Tests for the `add` function."""
# Test adding positve numbers
assert add(2, 2) == 4
# Test adding negative numbers
assert add(-2, -2) == -4
# Test adding floats
# assert add(2.7, 1.2) == 3.9
assert math.isclose(add(2.7, 1.2), 3.9)
# Test adding with 0
assert add(2, 0) == 2
# Run our test function
test_add()
Clicker Question #2¶
# Given the following function:
def divide_list(in_list):
output = []
for el1, el2 in zip(in_list[1:], in_list[0:-1]):
output.append(el1 / el2)
return output
divide_list((0,2,3))
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-27-c91f35be7f5f> in <module>()
----> 1 divide_list((0,2,3))
<ipython-input-18-1f783ab343a3> in divide_list(in_list)
4
5 for el1, el2 in zip(in_list[1:], in_list[0:-1]):
----> 6 output.append(el1 / el2)
7
8 return output
ZeroDivisionError: division by zero
# And the following test function:
def test_divide_list():
assert isinstance(divide_list([1, 2]), list)
assert divide_list([1, 2, 4]) == [2, 2]
test_divide_list()
A) These tests will pass, and this function is well tested
B) These tests will pass, but this function needs more tests
C) These tests will fail, but they cover the needed cases
D) These tests will fail, and we should also have more tests
PyTest¶
Levels of Code Testing:¶
Smoke Tests
Unit Tests
Integration Tests
System Tests
Test Driven Development¶
Test Driven Development¶
Ensures you go into writing code with a good plan / outline
Ensures that you have a test suite, as you can not decide to neglect test code after the fact
Note: when you complete (or at least write) assignments for this class, you are effectively doing test-driven development
Test Coverage¶
Writing Good Code¶
All in all, write code that is:
Documented
Well organized (follows a style guide)
Tested
And you will have understandable, maintainable, and trustable code.
Why We Write Tests:
ensure does does what it’s supposed to
system for checking things when you change / make updates in the future
Tests, when run, help identify code that will give an error if something has gone wrong.
Four general types¶
Smoke tests - preliminary tests to basic functionality; checks if something runs (but not necessarily if it does the right thing) (sanity check)
Unit tests - test functions & objects to ensure that they code is behaving as expected
Integration tests - tests functions, classes & modules interacting
System tests - tests end-to-end behavior
Unit Tests¶
one test for each “piece” of your code (each function, each class, each module, etc)
passes silently if true
error if it fails
consider “edge cases”
help you resist the urge to assume computers will act how you think it will work
functions used with pytest start with
test_
pytest
¶
check if error raised when expected to be raised
autorun all of your tests
formal testing to your code/projects
Executing pytest
¶
Look for any file called
test_...
If everything works, silently moves along.
For anything that fails, will alert you.
Available from Anaconda and on datahub
Clicker Question #3¶
Write a test function that checks the following piece of code:
A) I did it!
B) I think I did it!
C) I’m lost.
Thought process:
Define test function
def test_...
make
assert
ion within the test functioncheck that function sums the list (which was our expectation)
check that the input was a list (either in function or test function)
check the output is expected output / expected type
def sum_list(input_list):
"""add all values in a list - return sum"""
output = 0
for val in input_list:
output += val
return output
### YOUR TEST
def test_sum_list():
assert sum_list([1, 2, 3]) == 6
test_sum_list()
### POSSIBLE TEST
def test_sum_list():
# write multiple asserts
assert callable(sum_list)
assert isinstance(sum_list([1, 2, 3, 4]), int)
assert sum_list([1, 2, 3, 4]) == 10
test_sum_list()