Functions: Assertions and Testing

A Quick Review

Write a Function…

Example Usage:

>>> print(join_three_strings('one', 'two', 'three'))
one two three
>>> print(join_three_strings('1', '2', '3'))
1 2 3
>>> print(join_three_strings(1, 2, 3))
1 2 3

Let's write this function together →

Join Three Strings Solution

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

Syntax

A quick summary on syntax:

def <function_name>(<zero_or_more_parameters>):
	<statement #1>
	<statement #2>
	.
	.
	<etc.>

Parameters

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

What are the values of a, b and c in the above function when called as below:→

join_three_strings('one', 'two', 'three')
join_three_strings('three', 'one', 'two')
a = 'three'
b = 'two'
c = 'one'
join_three_strings(c, b, a)

Parameters Summary

Return Values

What's the difference between these two definitions of our function? What gets printed out for each code sample? →

Version #1

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

print(join_three_strings('one', 'two', 'three'))

Version #2

def join_three_strings(a, b, c):
	print("%s %s %s" % (a, b, c))

print(join_three_strings('one', 'two', 'three'))

Return Values Continued

The first definition (#1) returns a string, while the other (#2) doesn't return a value.

Printing out the results of both functions gives:

  1. Version #1
    • one two three
    • (this function returns a string, and the caller prints)
  2. Version #2
    • one two three
    • None
    • (this function prints out the string itself, and then returns no value, or None)

The Return Statement

Defining vs Calling

What gets printed out for each version of code?

#  version 1
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)
#  version 2
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

join_three_strings('one', 'two', 'three')
#  version 3
def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

print(join_three_strings('one', 'two', 'three'))

1: nothing, 2: nothing, 3: one two three

Testing Programs / Functions

So far, we've tested our programs by:

Some shortcomings of manual testing

What can we do to make testing less tedious and error prone? →

Assertions for Testing

Let's get the computer to test it for us! Assertions are a way to systematically check the state of our program.

Assertions as "Sanity Checks"

Assert Syntax

assert <some condition>, "a string representing a test"
  1. the keyword assert
  2. followed by any condition - an expression that returns True or False (usually expected == observed)
  3. followed by a comma
  4. followed by a string that represents the test

An Assertion Example

Let's use assertions to test an incorrect implementation of our function - one that doesn't have spaces between the strings. →

def join_three_strings(a, b, c):
	return "%s%s%s" % (a, b, c)

assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "should have spaces"
#  output shows line number and error...
Traceback (most recent call last):
  File "foo.py", line 4, in <module>
    assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "should have spaces"
AssertionError: joined string should have spaces

An Assertion Example That Actually Passes

Let's fix the program… and see what happens. →

def join_three_strings(a, b, c):
	return "%s %s %s" % (a, b, c)

assert "ha ha ha" == join_three_strings("ha", "ha", "ha"), "should have spaces"

#  results in no output

Another Assertion Example

Let's use assertions to test an incorrect implementation of an absolute_value function →

def absolute_value(x):
	if x >= 0:
		return x
	else:
		return -1
assert 1 == absolute_value(-1), "absolute value of negative # is positive"
assert 1 == absolute_value(1), "absolute value of positive # is same #"
assert 0 == absolute_value(0), "absolute value of 0 is 0"

Questions About Assertions

Documentation

Docstrings

def join_three_strings(a, b, c):
	"""returns a string composed of the values passed in separated by spaces""""
	return "%s %s %s" % (a, b, c)

In later classes, we'll see where this string shows up, but for now just use it as inline documentation for your code.