Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why an object instantiated in one Pytest impacts another instantiated in a different Pytest?

I am testing a class that contains a few methods for handling performance counters. It’s a normal Python class:

class S3Db:
    data = {}
    fs = None
    s3_path = None

    def __init__(self, s3_api_key, s3_api_secret, bucket_name, filename, bucket_path='/'):
        if s3_api_key != 'test':
            self.fs = s3fs.S3FileSystem(s3_api_key, secret=s3_api_secret)
            self.s3_path = f's3://{bucket_name}{bucket_path}{filename}'
            self.data = read_json_s3_path(self.fs, self.s3_path)
        pass

    def increment_timed_counter(self, counter_name: str):
        ...

My tests look like this (note the tested object is created again at the beginning of each test):

def test_can_increment_timed_counter():
    s3db = S3Db('test', 'testsecret', 'bucket', 'filename')

    s3db.init_timed_counter('Test Counter 1', 1000)

    val = s3db.increment_timed_counter('Test Counter 1')
    assert val == 1
    assert s3db.get_timed_counter('Test Counter 1') == 1


def test_can_init_timed_counters():
    # NOTE I CREATE A NEW INSTANCE HERE, SHOULD BE BRAND NEW?
    s3db = S3Db('test', 'testsecret', 'bucket', 'filename')

    s3db.init_timed_counter('Test Counter 2', 1000)

    assert s3db.data == {
        'timed_counters': {
            'Test Counter 2': {
                'name': 'Test Counter 2',
                'expiration_ms': 1000,
                'values': []
            }
        }
    }

But the second test fails with:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

E            'timed_counters': {'Test Counter 2': {'expiration_ms': 1000,
E                                                'name': 'Test Counter 2',
E         -                                      'values': []}},
E         ?                                                   -
E         +                                      'values': []},
E         +                     'Test Counter 1': {'expiration_ms': 1000,
E         +                                        'name': 'Test Counter 1',
E         +                                        'values': [854055045839803]}},
E           }

Indicating the counter created on the first test remains on the object being tested on the second test.

Why is this happening if I create a new object on the second test? Shouldn’t that first object be garbage collected and the second one be a brand new instance of that class?

I’m on Python 3.9

Thank you!

>Solution :

The variable declarations at the beginning of your class creates them as class data members which are shared by all instances. Therefore any modifications (e.g.: via a method call on one instance) will also be visible in all other instances, which can cause such bugs.

The fix is to ensure these members are instance members, e.g.:

class S3Db:
    def __init__(self, s3_api_key, s3_api_secret, bucket_name, filename, bucket_path='/'):
        self.data = {}
        self.fs = None
        self.s3_path = None
        if s3_api_key != 'test':
            self.fs = s3fs.S3FileSystem(s3_api_key, secret=s3_api_secret)
            self.s3_path = f's3://{bucket_name}{bucket_path}{filename}'
            self.data = read_json_s3_path(self.fs, self.s3_path)
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading