Emulating the Python unittesting module for non-unit test applications.
Description
Here’s a code snippet for my future self. The base python unit testing module is great and I will not claim to know all of it’s features. But I know that it’s not exactly what I want to use for tests that aren’t quite ‘unit tests’. There is probably some software developer-y word for this, but I often want to have codes that perform ‘idiot checks’ on the inputs to the numerical models that I run (filepaths exists, configuration parameters are reasonable, etc). So to do this I wrote a class that emulates the some of the behavior of the unit testing module. In the ‘Check’ class below, all of the functions that start with the word ‘_test’ will be executed. So it’s quite easy to add new tests as they arise (i.e., I find new ways to make things break). The ‘passfial’ decorator catches the traceback string and hands it off, in addition a boolean pass/fail status.
import traceback
# Function decorator
def passfail(func):
def wrapped_func(*args, **kwargs):
try:
func(*args)
message = "{} Passed".format(str(func))
return (True,message)
except Exception as e:
trace_string = traceback.format_exc()
error_message = "{} Failed with the following Error: {}\n {}".format(str(func), e, trace_string)
return (False, error_message)
return wrapped_func
class Check():
# checks that the config.yaml file is sensible (paths exist, etc)
def __init__(self):
# OPTIONALLY DO SOME STUFF
# In reality here I'm inheriting the 'setup' class for this
# partifular process, but this is ommitted for simplicity.
pass
@passfail
def test_foo(self):
assert 1 == 2, "MESSAGE"
@passfail
def test_bar(self):
assert 1 == 2, "MESSAGE"
def run_all(self):
# Emulate behavior of the unittesting module
# Run every function definted in the 'Check' class whos name starts with 'test_'
testList = [method for method in dir(self.__class__) if method.startswith('test_')]
numTests = len(testList)
numPassedTests = 0
# Run all of the tests
for test in testList:
testFunction = getattr(self.__class__, test)
success,status = testFunction(self)
if success: print('success', status)
if not success: print('error', status)
numPassedTests += success # zero or one
print("{} out of {} tests passed".format(numPassedTests, numTests))
# Return status of test passing
# This can be caught by and used to determine if the process proceeds or not
if numPassedTests != numTests:
return False
else:
return True