Files
notifications-admin/tests/app/models/test_base_model.py
Chris Hill-Scott 23e1682260 Make dynamic attributes of model introspectable
`dir(object)` is a useful Python function that tells you what attributes
and methods an object has. It’s also used by tools like iPython and IDEs
for code completion.

Some of the attributes of a `JSONModel` are dynamic, based on what
fields we expect in the underlying JSON. Therefore they don’t
automatically end up in the result of calling `dir`. To get around this
we can implement our own `__dir__` method, which also returns the names
of the fields we’re expecting the the JSON.

Inspired by this Raymond Hettinger tweet:

> #python tip:  If you add attributes to an API with __getattr__() or
> __getattribute__(), remember to update __dir__() to make the extension
> introspectable.

— https://twitter.com/raymondh/status/1249860863525146624
2020-04-14 09:44:38 +01:00

79 lines
1.7 KiB
Python

import pytest
from app.models import JSONModel
def test_looks_up_from_dict():
class Custom(JSONModel):
ALLOWED_PROPERTIES = {'foo'}
assert Custom({'foo': 'bar'}).foo == 'bar'
def test_prefers_property_to_dict():
class Custom(JSONModel):
ALLOWED_PROPERTIES = {'foo'}
@property
def foo(self):
return 'bar'
assert Custom({'foo': 'NOPE'}).foo == 'bar'
@pytest.mark.parametrize('json_response', (
{},
{'foo': 'bar'}, # Should still raise an exception
))
def test_model_raises_for_unknown_attributes(json_response):
model = JSONModel(json_response)
assert model.ALLOWED_PROPERTIES == set()
with pytest.raises(AttributeError) as e:
model.foo
assert str(e.value) == (
"'JSONModel' object has no attribute 'foo' and 'foo' is not a "
"field in the underlying JSON"
)
def test_model_raises_keyerror_if_item_missing_from_dict():
class Custom(JSONModel):
ALLOWED_PROPERTIES = {'foo'}
with pytest.raises(KeyError) as e:
Custom({}).foo
assert str(e.value) == "'foo'"
@pytest.mark.parametrize('json_response', (
{},
{'foo': 'bar'}, # Should be ignored
))
def test_model_doesnt_swallow_attribute_errors(json_response):
class Custom(JSONModel):
@property
def foo(self):
raise AttributeError('Something has gone wrong')
with pytest.raises(AttributeError) as e:
Custom(json_response).foo
assert str(e.value) == 'Something has gone wrong'
def test_dynamic_properties_are_introspectable():
class Custom(JSONModel):
ALLOWED_PROPERTIES = {'foo', 'bar', 'baz'}
assert dir(Custom({}))[-3:] == ['bar', 'baz', 'foo']