1

I started with a class representing a DynamoDB database table:

from pynamodb.models import Model


class FooModel(Model):
    class Meta:
        table_name = "foo"
    …

I've got several tests involving this class. Some of them use FooModel instances in such a way as to never actually talk to the cloud (I've ensured this by using pytest-socket's --disable-socket flag).

The problem is that for Reasons™ I have changed the table name to be generated by the cloud provider. This means that any production code which actually needs the table_name must talk to the cloud provider. Now, if I simply do table_name = get_table_name(…) the offline tests will fail, because that code is executed at import time. I still want the unit tests to run offline, however, so I've changed the field into a property which calls a method to look up the actual value at runtime:

        @property
        @classmethod
        def table_name(cls) -> str:
            return get_table_name(…)

This makes the offline tests pass without introducing any complex mocking frameworks or running a different code path during tests and production. The problem is that the acceptance tests (which run against a deployed cloud infrastructure) are failing because boto3 is trying and failing to serialize the property into JSON:

self = <json.encoder.JSONEncoder object at 0x7f65ad5b0400>, o = <property object at 0x7f65ab46d450>

    def default(self, o):
        """Implement this method in a subclass such that it returns
        a serializable object for ``o``, or calls the base implementation
        (to raise a ``TypeError``).
    
        For example, to support arbitrary iterators, you could
        implement default like this::
    
            def default(self, o):
                try:
                    iterable = iter(o)
                except TypeError:
                    pass
                else:
                    return list(iterable)
                # Let the base class default method raise the TypeError
                return JSONEncoder.default(self, o)
    
        """
>       raise TypeError(f'Object of type {o.__class__.__name__} '
                        f'is not JSON serializable')
E       TypeError: Object of type property is not JSON serializable

../../.pyenv/versions/3.8.6/lib/python3.8/json/encoder.py:179: TypeError

How do I work around this without introducing any more frameworks and making sure the production code path remains the same as the test code path?

Related issue & actual code in case you want to look at either.

l0b0
  • 55,365
  • 30
  • 138
  • 223
  • "I started with a class representing a database table in the cloud:" What framework(s) are you using? You should tag appropriately. I also removed the [python-3.8] tag because there is thus far no reason to suspect the problem is specific to the Python version. – Karl Knechtel Mar 18 '21 at 22:27
  • @KarlKnechtel Updated. – l0b0 Mar 18 '21 at 22:30

0 Answers0