Using the Command Pattern to Write More Testable Python

Often times, when writing Python, I run into a situation that requires me to write a simple validation function.

def check_validity(item):
	return item.value_to_check > 0

This function is easy to test, and it’s clear what it’s supposed to be doing. But as so often happens, I may need to validate more than the single attribute I originally checked.

def check_validity(item):
	valid = False
	if item.value_to_check > 0:
		valid = True
	if valid and item.another_value_to_check < 0:
		valid = False
	return valid

Now this function has become quite a bit more complex. However, since its only responsibility is to validate that item is still valid and that I can still test it, it's really easy to talk myself into accepting the code as it is.

def check_validity(item):
	valid = False
	if item.value_to_check > 0:
		valid = True
	if valid and item.another_value_to_check < 0:
		valid = False
	if valid:
		for value in item.list_to_check:
			if value < 0:
				valid = False
				break
	return valid

Despite the fact that I can still test this code, there are a couple of problems that I can no longer overlook. Namely, I've introduced state and nested if and for statements. But what do I do about it?

def check_validity(item):
	def value_is_valid(value):
		return value > 0
	def check_list(value_list):
		for value in item.list_to_check:
			if value < 0:
				valid = False
				break
	return value_is_valid(item.value_to_check) and\
		value_is_valid(item.another_value_to_check) and\
		check_list(item.list_to_check)

This is an improvement, since I've compartmentalized the outer if statements into functions of their own, as well as removed the valid variable. The problem, however, is that despite the fact that I can test check_validity, I can't test either of the nested functions. If one of those functions grows in complexity, there's an excellent chance that a bug might slip my notice.

Thankfully, I can turn to the Command Pattern.

class CheckValidityCommand(object):
	def _value_is_valid(self, value):
		return value > 0
	def _check_list(self, value_list):
		for value in item.list_to_check:
			if value < 0:
				valid = False
				break
	def __call__(self, item):
		return self._value_is_valid(item.value_to_check) and\
			self._value_is_valid(item.another_value_to_check) and\
			self._check_list(item.list_to_check)

By refactoring the check_validity function into a class, we now have direct access to those nested functions that were previously hidden from us. Furthermore, consuming the class is really simple.

check_validity = CheckValidityCommand()
assert(check_validity(item_that_passes) == True)
assert(check_validity(item_that_fails) == False)