SQLAlchemy的模拟默认属性

时间:2019-01-18 17:45:33

标签: python testing flask sqlalchemy pytest

在模型中使用defaultonupdate字段时,我在模拟SQLAlchemy对象时遇到一些问题:

def get_uuid():
    return str(uuid.uuid4())

def get_now():
    return db.func.now()

class BaseModel(db.Model):
    __abstract__ = True

    id = db.Column(UUIDType(binary=False), primary_key=True, nullable=False, default=get_uuid)
    created_at = db.Column(db.DateTime(timezone=True), default=get_now(), nullable=False, index=True)

即使尝试在测试中模拟它们,get_now()get_uuid()的行为也不会改变:

def test_create_source(client, mocker):

    mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
    mocker.patch('myproject.models.get_uuid', mock)
    mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
    mocker.patch('myproject.models.get_now', mock)

    resp = client.post('/sources', json={'name': 'My source'})
    assert resp.json == {
        'name': 'My source',
        'id': '123e4567-e89b-12d3-a456-426655440000',
        'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
        'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
    }

### Results :

>       assert resp.json == {
            'name': 'My source',
            'id': '123e4567-e89b-12d3-a456-426655440000',
            'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT',
            'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'
        }
E       AssertionError: assert {'createdAt':...17:38:38 GMT'} == {'createdAt': ...00:00:00 GMT'}
E         Omitting 1 identical items, use -vv to show
E         Differing items:
E         {'id': '8eb074c0-41e9-436c-8f71-b4c6842f4809'} != {'id': '123e4567-e89b-12d3-a456-426655440000'}
E         {'createdAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'createdAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
E         {'updatedAt': 'Fri, 18 Jan 2019 17:38:38 GMT'} != {'updatedAt': 'Tue, 01 Jan 2019 00:00:00 GMT'}
E         Use -v to get the full diff

tests/test_sources.py:17: AssertionError

我认为这是因为在进行测试之前,我已经导入了模型及其属性并对其进行了评估,因此此处的模拟无效。 post的“模拟类帮助器”部分对此进行了解释,但是我仍然无法解决我的问题:(

重现此问题的完整可运行代码可在此处找到:https://github.com/ncrocfer/flaskmock

请问您有什么主意吗?

1 个答案:

答案 0 :(得分:0)

  

我认为这是因为我的模型及其属性已经在执行测试之前导入并评估了,因此此处的模拟无效。

关闭:您已将对函数get_uuid()的引用默认传递给id,并将对get_now()(函数表达式对象)的调用传递给默认值created_at 。在事实发生之后,更改这些名称在模块中的绑定方式将不再对已包含对象本身引用的列产生影响。

对于get_uuid(),您应该模拟它正在调用的函数:

mock = mocker.MagicMock(return_value='123e4567-e89b-12d3-a456-426655440000')
mocker.patch('uuid.uuid4', mock)

,如果使用get_now(),则应考虑在模型构建期间不调用它:

class BaseModel(db.Model):
    ...
    created_at = db.Column(..., default=get_now, ...)

,以便您可以再次模拟它正在调用的实际功能:

mock = mocker.MagicMock(return_value=datetime.datetime(2019, 1, 1))
mocker.patch('sqlalchemy.func.now', mock)

另一方面,也许您不应该在测试中声明id和时间戳,因为看来重要的是要验证名称的正确设置。