Search code examples
pythonclasspython-unittestpython-class

How to define class instances per unit test?


I have the following class, and I would like to write several unit tests for it:

class JsonSchemaValidator:
    def __init__(self, json_file):
        self.json_file = json_file
        self.schema = json.load(open(json_file))

    def check_json_schema(self):
        print(self.json_file)
        Draft3Validator.check_schema(self.schema)

As shown above, the class has two instances of self.json_file and self.schema, and I would like to define the schema per test. How can I set up the tests, so schema can be defined per test case?

class TestValidator(TestCase):
    def test_check_json_schema1(self):
        schema = {
            "type": "object",
            "properties": {
                "key1": {"type": "string"}
            },
        }
        actual = JsonSchemaValidator.check_json_schema() ##??
        self.assertIsNone(actual)

    def test_check_json_schema2(self):
        schema = {
            "type": "object",
            "properties": {
                "key2": {"type": "SOME_TYPE"}
            },
        }
        self.assertRaises(SchemaError, JsonSchemaValidator.check_json_schema, schema) ##??

Solution

  • The problem is that you don't want your code to actually open a file on the disk and load it, you want to simply provide the result. One way to do that is to mock the open and json.load references that TestValidator uses, like that :

    import json
    import unittest
    import unittest.mock as mocking
    
    
    class JsonSchemaValidator:
        def __init__(self, json_file_path):
            self.json_file_path = json_file_path
            self.schema = json.load(open(json_file_path))
    
        def check(self):
            print(self.json_file_path)
            # do the validation here
    
    
    class TestValidator(unittest.TestCase):
        def test_check_json_schema1(self):
            schema = {
                "type": "object",
                "properties": {
                    "key1": {"type": "string"}
                },
            }
            with mocking.patch("builtins.open"), \
                    mocking.patch.object(json, "load", new=mocking.Mock(return_value=schema)):
    
                validator = JsonSchemaValidator("/blah")
                print(validator.schema)  # exactly what we wanted : {'type': 'object', 'properties': {'key1': {'type': 'string'}}}
    
                # do your test here
                validator.check()
                ...
    

    You can check by adding print(json.load, open) to JsonSchemaValidator.__init__, you will get something like :

    <Mock id='139724038827840'> <MagicMock name='open' id='139724039146992'>
    

    because they have been mocked while you were in the context manager (with).

    (I renamed json_file to json_file_path because I think it makes things more clear)