Categories

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