Class to unit-test functions, with rudimentary support for testing multi-parameter functions Recently I read about test-driven-design and decided to try to create a simple unit testing class to begin using this approach. I have not been programming in Python for very long so forgive my ignorance of the language. 

I would like to discuss better approaches to handling multi-parameter test functions. Right now I accept a tuple (of varying length) with one or more function inputs. 

I find the tuple input and "length" checks (lines 10-13) to be clunky. Is there a better way to handle this? I tried using a spread operator to distribute the arguments into the test function but this fails for list inputs (it spreads the list input itself rather than inserting it as the argument).

Like I said this is very basic and I'm sure there are modules out there I can import to do a much more thorough job. I learn best when I try something myself so while I appreciate any suggestions for external modules I'd like to focus on improving this class itself. 

class Unit_Tester:
  def __init__(self, function, tests):
    self.function_to_test = function
    self.test_list = tests

  def unit_test(self, function_to_test, test_input, expected_output):
      try:
          number_of_inputs = len(test_input)
          if number_of_inputs == 1: function_output = function_to_test(test_input)
          elif number_of_inputs == 2: function_output = function_to_test(test_input[0], test_input[1])
          elif number_of_inputs == 3: function_output = function_to_test(test_input[0], test_input[1], test_input[2])
          elif type(test_input) == list: function_output = function_to_test(test_input)
      except Exception as error:
          print("Error occurred with test input: [{0}] value: {1}\nError Message: {2}\nCorrect the error and try again.\n"
          .format(type(test_input), test_input, error))
      else:
        try:
            assert function_output == expected_output
            print(self.unit_test_response(True, test_input, function_output, expected_output))
        except AssertionError:
            print(self.unit_test_response(False, test_input, function_output, expected_output))

  def unit_test_response(self, correct, test_input, function_output, expected_output): 
      score = "Test Passed" if correct else "Test Failed" 
      return "{0}\nInput: {1}\nExpected Output: {2}\nFunction Output: {3}\n".format(score, test_input, expected_output, function_output)

  def run_unit_tests(self, function_to_test, test_list):  
      for test_tuple in test_list:
          test_input, expected_output = test_tuple
          self.unit_test(function_to_test, test_input, expected_output)

  def run(self):
    self.run_unit_tests(self.function_to_test, self.test_list)
Since indentation is crucial in Python, you should consistently use four spaces, as specified in PEP 8, the official Python style guide.

Also, don't mix naming styles.  Based on PEP 8, the class should be named UnitTester.



Your test harness neglects to support…


calling functions with no parameters, or with more than 3 parameters.  This is easily solved using the * operator:

function_output = function_to_test(*test_input)

calling functions with keyword parameters.  You could accomplish this using the ** operator.
testing that a function raises an exception as expected.  That is left as an exercise for you.
testing whether a function performs expected side-effects, such as printing certain output.  This is quite tricky to implement, but it's something that doctest can easily handle.

def aloha():
    """
    &gt;&gt;&gt; aloha()
    Hello
    'Goodbye'
    """
    print('Hello')
    return "Goodbye"





This code is an abuse of assertions:


    try:
        assert function_output == expected_output
        print(self.unit_test_response(True, test_input, function_output, expected_output))
    except AssertionError:
        print(self.unit_test_response(False, test_input, function_output, expected_output))



Assertions are a way for you, as a programmer, to state that a certain condition must logically be true, not that you hope for a condition to be true.

Furthermore, assertions can be disabled, in which case every test would succeed!

You should replace that code with:

      result = (function_output == expected_output)
      print(self.unit_test_response(result, test_input, function_output, expected_output))




My biggest complaint about this unit-testing class, though, is that it is unclear how it is meant to be used.  You didn't describe the intended usage in the question, and you didn't write docstrings like you should have.  I'm guessing that this is your intended usage:

Unit_Tester(max, [
    ([3, 1, 4], 4),
    ([-3, -1, -4], -1),
]).run()


But why does the run_unit_tests(…) method exist?  Is this a "valid" way to use your harness?

Unit_Tester(None, None).run_unit_tests(max, [
    ([3, 1, 4], 4),
    ([-3, -1, -4], -1),
])


If it is, then I recommend getting rid of the tests parameter in the constructor.  If it isn't, then I recommend merging run_unit_tests() into run(), so that misuse is impossible.

Alternatively, name your "private" methods with a leading underscore (e.g. _unit_test_response(…) to indicate that a method should not be called directly.