Unittest - data driven edge-case tests with objects?

655 views Asked by At

Consider the following:

import unittest

class MyClass:
    def __init__(self, dict: {}):
        try:
            if self.validate_dict(dict):
                self.data = dict
        except Exception as e:
            raise Exception('Unable to create MyClass: {}'.format(str(e)))

    def validate_dict(self, dict: {}):
        if 'special_key' not in dict:
            raise Exception('Missing Key: special_key')

        # ... perhaps more complicated validation code...
        return True

class MyTests(unittest.TestCase):
    def test_bad_validation(self):
        with self.assertRaises(Exception) as context:
            test_dict = {}
            test_class = MyClass(test_dict)

        self.assertTrue('Unable to create' in str(context.exception))

... assuming this isn't a terrible approach for unit-testing this function, how might I add more test cases to the unittest apart from just {}?

I feel like it would be useful to see a nice list of all the test-cases of a particular test at a glance and quickly add new ones as well.

I found a library DDT that was made to tackle this very issue, but it doesn't seem that there is any way to pass an entire object, like a dict, as a test arguement without unpacking it. In this case I want to test for the existence of keys (and there might be many) so unpacking them into individual arguements like DDT does seems like a poor solution.

Does unittest support something like this? I know pytest does but I wanted to see whats possible with unittest first.

Other approaches to unit-testing this code are also appreciated.

1

There are 1 answers

0
Terry Jan Reedy On BEST ANSWER

I believe you are looking for the subTest method (added in 3.4).

import unittest

class MyClass:
    def __init__(self, dic: {}):
        try:
            if self.validate_dict(dic):
                self.data = dic
        except KeyError as e:
            raise ValueError('Unable to create MyClass: {}'.format(e))

    def validate_dict(self, dic):
        if 'special_key' not in dic:
            raise KeyError('Missing Key: special_key')

        # ... perhaps more complicated validation code...
        return True

class MyTests(unittest.TestCase):
    def test_bad_validation(self):
        for test_dict in (
                {},
                {'a':1},
                {'b':2, 'else':3},
                {'special_key':4},
                ):
            with self.subTest(test_dict=test_dict):
                with self.assertRaises(Exception) as context:
                    MyClass(test_dict)
                self.assertTrue('Unable to create' in str(context.exception))

unittest.main()

In 3.6, this prints, for the case that fails by passing the validation check:

======================================================================
FAIL: test_bad_validation (__main__.MyTests) (test_dict={'special_key': 4})
----------------------------------------------------------------------
Traceback (most recent call last):
  File "F:\Python\mypy\tem2.py", line 23, in test_bad_validation
    MyClass(test_dict)
AssertionError: Exception not raised

----------------------------------------------------------------------
Ran 1 test in 0.014s

FAILED (failures=1)