moto icon indicating copy to clipboard operation
moto copied to clipboard

s3 bucket state isn't persisting as I expect

Open BenjaminCaffrey opened this issue 3 years ago • 7 comments

I'm primarily referencing this Class Decorator piece of documentation. This leads me to believe that state created or modified in setUp(self): will persist through tests. I run that code and it looks great.

Please let me know if this is a fundamental Python or unittest misunderstanding, but here is my snippet:

import unittest
import boto3
from moto import mock_s3


@mock_s3
class TestMotoBugBaseClass:
    def setUp(self):
        client = boto3.client("s3")
        client.create_bucket(Bucket="test_bucket")

        num_buckets = len(
            [bucket.name for bucket in boto3.resource("s3").buckets.all()]
        )
        self.assertEqual(num_buckets, 1)  # Looks like we have a bucket!

    def test_bug(self):
        num_buckets = len(
            [bucket.name for bucket in boto3.resource("s3").buckets.all()]
        )
        self.assertEqual(num_buckets, 1)


class TestInheritingClass(TestMotoBugBaseClass, unittest.TestCase):
    pass

Expected: I expect that the bucket we create as part of the setUp method will persist into the test_bug method.

Actual: This test fails - we see 0 buckets returned during execution of test_bug.


Version: I have tried this in both 1.3.14 and the most recent 3.1.1 with similar results.

Thank you for any guidance! Appreciate working with moto so far, slightly confused on by this particular behavior.

BenjaminCaffrey avatar Mar 24 '22 21:03 BenjaminCaffrey

Hi @BenjaminCaffrey, welcome to Moto! It does work like this, by extending the BaseClass with the unittest.TestCase-class:

@mock_s3
class TestMotoBugBaseClass(unittest.TestCase):
    ...

Note that executing the TestMotoBugBaseClass-class also fails in this non-Moto example, because the setUp-method is not only executed for classes that extend unittest.TestCase.

class TestMotoBugBaseClass:
    def setUp(self):
        self.x = True

    def test_bug(self):
        print(self.x)


class TestInheritingClass(TestMotoBugBaseClass, unittest.TestCase):
    pass

bblommers avatar Mar 24 '22 21:03 bblommers

Having said that, looking at your example, running TestInheritingClass should pass, as this does extend the unittest.TestCase. I'll do some debugging to see why this fails.

bblommers avatar Mar 24 '22 21:03 bblommers

Thank you @bblommers ! Let me know if there's anything I can do to help.

To clarify my understanding of the situation:

In your snippet, if we were to run python -m unittest on the file, we run TestInheritingClass (because it inherits from unittest.TestCase, which will call setUp and subsequently test_bug (because it also inherits from TestMotoBugBaseClass, and we see that self.x = True. So in that case test_bug is sort of "state aware" of what happened in SetUp.

But when we do the same thing with moto and mocked boto3 state (the snippet I posted), it doesn't seem to be the case.

BenjaminCaffrey avatar Mar 24 '22 22:03 BenjaminCaffrey

Yes, that is correct.

The class-decorator is responsible for resetting the state between tests. On basic classes, it will reset before every test-method. If the class extends unittest.TestCase, it will reset the state before the setUp-method instead.

The underlying issue is that the class-decorator acts on compile-time. The only context we have at that point, is that we are extending TestMotoBugBaseClass - but Moto doesn't know that we will be extending TestCase.

It may be possible to change the implementation, so that we figure this out at run-time. At run-time we do know whether we are extending TestCase. Considering that there is a workaround, and the potential to introduce other bugs, I'm going to consider this low-priority though.

bblommers avatar Mar 26 '22 10:03 bblommers

Hi @bblommers !

Have you had any time to look into this since this issue was last updated ? :)

Seluj78 avatar Nov 25 '23 08:11 Seluj78

Not really @Seluj78 - I haven't found a good solution to this problem yet

bblommers avatar Nov 25 '23 20:11 bblommers

I understand. For now I guess I will have to decorate each class individually :/

Although if you do find any idea on how to solve this, I'd love to hear it! I am certain it would be a really appreciated feature for unit test users 😉

Seluj78 avatar Nov 25 '23 20:11 Seluj78