testing - 试图在 Flask-SQLAlchemy 中模拟模型的问题

标签 testing flask mocking flask-sqlalchemy

我正在使用 Flask-SQLAlchemy 测试具有一些 SQLAlchemy 模型的 Flask 应用程序,我在尝试将一些模型模拟为某些接收某些模型作为参数的方法时遇到了一些问题。

我正在尝试做的玩具版本是这样的。假设我有一个模型:

// file: database.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()  

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    birthday = db.Column(db.Date)

在使用应用工厂模式构建的应用中导入:

// file: app.py
from flask import Flask
from database import db

def create_app():
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
    db.init_app(app)

还有一些函数需要一个User作为参数:

// file: actions.py
import datetime

SECONDS_IN_A_YEAR = 31556926

def get_user_age(user):
    return (datetime.date.today() - user.birthday).total_seconds() //  SECONDS_IN_A_YEAR

此外,应该有一些 View 和蓝图在 app.py 中导入并在应用程序中注册,后者在某处调用函数 get_user_age

我的问题是:我想测试函数 get_user_age 而不必创建应用程序、注册假数据库等等。这不是必需的,该函数是完全独立的因为它在 Flask 应用程序中使用。

所以我尝试了:

import unittest

import datetime
import mock

from database import User
from actions import get_user_age

class TestModels(unittest.TestCase):
    def test_get_user_age(self):
        user = mock.create_autospec(User, instance=True)
        user.birthday = datetime.date(year=1987, month=12, day=1)
        print get_user_age(user)

这引发了一个 RuntimeError: application not registered on db instance and no application bound to current context 异常。所以我想“是的,显然我必须修补一些对象以防止它检查应用程序是否已在数据库中注册等”。所以我尝试用 @mock.patch("database.SQLAlchemy") 和其他东西装饰它,但无济于事。

有谁知道我应该修补什么来防止这种行为,或者即使我的测试策略完全错误?

最佳答案

所以,我在键盘上敲了几个小时后找到了解决方案。问题好像是下面的(有知道的请指正)。

当我运行 mock.create_autospec(User) 时,mock 模块会尝试检查 User 的所有属性,以便为它将吐出的 Mock 对象创建足够的规范.发生这种情况时,它会尝试检查属性 User.query,只有当您在 Flask 应用程序的范围内时才能对其进行评估。

发生这种情况是因为在评估 User.query 时,会创建一个需要有效 session 的对象。该 session 由 Flask-SQLAlchemy 中 SQLAlchemy 类的 create_scope_session 方法创建。

此方法实例化一个名为 SignallingSession 的类,其 __init__ 方法调用 SQLAlchemy.get_app 方法。这是在数据库中没有注册应用程序时引发 RuntimeError 的方法。

通过修补 SignallingSession 方法,一切正常。因为我不想与数据库交互,所以没关系:

import unittest
import datetime

import mock

from actions import age


@mock.patch("flask_sqlalchemy.SignallingSession", autospec=True)
class TestModels(unittest.TestCase):

    def test_age(self, session):
        import database

        user = mock.create_autospec(database.User)
        user.birthday = datetime.date(year=1987, month=12, day=1)
        print age(user)

关于testing - 试图在 Flask-SQLAlchemy 中模拟模型的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28192283/

相关文章:

python - 在 Flask 中更改每个请求的日志级别

java - 如何使用 (Power)Mockito 在类初始化中模拟私有(private)静态方法的静态调用?

java - 如何使用 Cobertura 检测多项目 Maven 2?

testing - Groovy REST 端点测试替代方案

spring - 为什么在 Spring Boot 中有不同类型的集成测试

python - Python Flask 上有单例模式吗?

java - 如何使用 JUnit5 测试抽象接口(interface)?

python - 使用 Flask 和 gunicorn 进行 JSON 格式的日志记录

python - 如何模拟 boto3 客户端对象/调用

python - 模拟类和断言方法调用