Search code examples
pythonunit-testingddt

How do I set up the test data and the test cases in a single python script


I am writing a unit test and trying to set up the test data to use for the test cases in the same python script.

However, When I run the script, it does create the test data, but prints an error message that the data do not exist, leading to the test failure. It's only when I run the script again that the test succeeds.

Below is a simplified script that I wrote to figure out what's going on.

import unittest
from ddt import ddt, file_data
import pandas

@ddt
class TestWhatever(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.setup_test_data()
        print("setUpClass is running")

    @classmethod
    def tearDownClass(cls):
        print("tearDownClass is running")

    @classmethod
    def setup_test_data(cls):
        data = pandas.DataFrame({'msg':["testing"]})
        data = data.transpose()
        with open("practice_test.json", "w") as file:
           file.write(data.to_json())
        print("setup_test_data is running")

    @file_data("practice_test.json")
    def test_whatever_possible(self, msg):
        print("test_whatever_possible is running :", msg)
        self.assertEqual('q', 'q')

    def test_whatever_impossible(self):
        print("test_whatever_impossible is running")
        self.assertEqual('n', 'n')

When I run the script above, it prints :

setup_test_data is running
setUpClass is running
test_whatever_impossible is running
.EtearDownClass is running

======================================================================
ERROR: test_whatever_possible_00001_error (main.TestWhatever)
Error!
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\ddt.py", line 145, in wrapper
    return func(self, *args, **kwargs)
  File "C:\ddt.py", line 187, in func
    raise ValueError(message % file_attr)
ValueError: practice_test.json does not exist

----------------------------------------------------------------------
Ran 2 tests in 0.006s

FAILED (errors=1)

Then on the second run :

setup_test_data is running
setUpClass is running
test_whatever_impossible is running
.test_whatever_possible is running : testing
.tearDownClass is running

----------------------------------------------------------------------
Ran 2 tests in 0.005s

OK

I'm pretty much lost at this point... Does anyone have a clue on this?


Solution

  • As a workaround you could write your own decorator to create the data file e.g.

    def create_data(name):
        def decorator(fn):
            def decorated(*args, **kwargs):
                data = pandas.DataFrame({"msg": ["testing"]})
                data = data.transpose()
                with open(name, "w") as file:
                    file.write(data.to_json())
                res = fn(*args, **kwargs)
                os.unlink(name)
                return res
            return decorated
        return decorator
    

    and then stack this for your test (and remove the setup_test_data step):

    @ddt
    class TestWhatever(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            print("setUpClass is running")
    
        @classmethod
        def tearDownClass(cls):
            print("tearDownClass is running")
    
        @create_data("practice_test.json")
        @file_data("practice_test.json")
        def test_whatever_possible(self):
            print("test_whatever_possible is running :")
            self.assertEqual("q", "q")
    

    Update: Jan 31st

    The following example preserves the ddt behaviour of passing the parameter value into the test function:

    import json
    import unittest
    from ddt import ddt, FILE_ATTR
    
    
    def create_file_data(value, data):
        def wrapper(func):
            with open(value, "w") as file:
                file.write(json.dumps(data))
            # this is what the file_data decorator does
            setattr(func, FILE_ATTR, value)
            return func
    
        return wrapper
    
    
    @ddt
    class TestWhatever(unittest.TestCase):
        @create_file_data("practice_test.json", {"msg": ["testing"]})
        def test_file_data(self, msg):
            print("test_file_data msg:", msg)
    
    
    unittest.main()
    

    When this is run the output is:

    test_file_data msg: ['testing']
    .
    ----------------------------------------------------------------------
    Ran 1 test in 0.001s
    

    Using the two examples above you should be able to derive an appropriate solution for your problem.