在Flask测试案例会话中无法检索数据库对象

时间:2018-06-25 03:59:23

标签: python unit-testing flask sqlalchemy factory-boy

我正在测试使用flask-restfulsqlalchemyflask-sqlalchemyfactory-boy创建的API。

我遇到了一个奇怪的问题,在使用GET的{​​{1}}请求之前创建的对象可用,但在post / put调用之前创建的对象不可用。

我已经基于factory建立了一个测试用例类:

flask-testing

测试工厂:

# tests/common.py
from flask_testing import TestCase as FlaskTestCase

from app import create_app
from database import db
from config import TestConfig


class TestCase(FlaskTestCase):
    def create_app(self):
        return create_app(TestConfig)

    def setUp(self):
    db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

资源:

# tests/factory.py

import factory

from database import db

from ..models import Item

class ItemFactory(factory.alchemy.SQLAlchemyModelFactory):
    class Meta:
        model = Item
        sqlalchemy_session = db.session

测试:

# resources.py
from flask import request
from flask_restful import Resource

from database import db
from .models import Item
from .serializers import ItemSerializer

class ItemResource(Resource):
    def get(self, symbol):
        obj = db.session(Item).filter(Item.symbol == symbol).first_or_404()
    return ItemSerializer(obj).data

    def put(self, symbol):
        params = request.get_json(silent=True)
        query = db.session(Item).filter(Item.symbol == symbol).update(params)

        db.session.commit()

        obj = db.session(Item).filter(Item.symbol == symbol).first_or_404()
        return ItemSerializer(obj).data

# tests/test_resources.py import json from tests.common import TestCase from tests.factory import ItemFactory def parse_response(response): return json.loads(response.get_data().decode()) class ResourcesTest(TestCase): def test_get_item(self): symbol = 'TEST' ItemFactory(symbol=symbol) response = self.client.get('/api/v1/items/%s' % symbol) results = parse_response(response) self.assertEqual(response.status_code, 200) self.assertEqual(results['symbol'], symbol) def test_update_item(self): symbol = 'TEST' new_symbol = 'TEST_NEW' ItemFactory(symbol=symbol) response = self.client.put('/api/v1/items/%s' % symbol, json={'symbol': new_symbol}) results = parse_response(response) self.assertEqual(response.status_code, 200) 中,我收到404。在输入test_update_item之前,检查数据库显示新的self.client.put已创建。但是,当我们在资源中到达Item方法时,put返回一个空数组。

db.session(Item).query.all()文档中,我发现了这一段:

flask-testing

我认为问题在于会话的处理方式,但无法提出任何解决方案来确保我的测试通过。另一个有趣的观察结果是,对于 Another gotcha is that Flask-SQLAlchemy also removes the session instance at the end of every request (as should any thread safe application using SQLAlchemy with scoped_session). Therefore the session is cleared along with any objects added to it every time you call client.get() or another client method. self.client.put,如果我将发布的数据更改为self.client.post,则对象将在会话中可用。

2 个答案:

答案 0 :(得分:1)

好的,找到了解决这个问题的复杂方法。我认为调用request.get_json会在其他地方实例化会话。解决方案是重写json数据的发布方式。

tests/common.py中:

import json

from flask_testing import TestCase as FlaskTestCase
from flask.testing import FlaskClient

from app import create_app
from database import db
from config import TestConfig


class TestClient(FlaskClient):
    def open(self, *args, **kwargs):
        if 'json' in kwargs:
            kwargs['data'] = json.dumps(kwargs.pop('json'))
            kwargs['content_type'] = 'application/json'
        return super(TestClient, self).open(*args, **kwargs)


class TestCase(FlaskTestCase):
    def create_app(self):
        app = create_app(TestConfig)
        app.test_client_class = TestClient
        return app

    def setUp(self):
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

因此,基本上,我们将测试客户端中的post / put调用中的任何json kwargs转换为数据,并将content_type设置为json。在这里(https://stackoverflow.com/a/40688088)偶然发现了这一点。

答案 1 :(得分:0)

您可能需要看一下SQLAlchemy会话处理。

factory_boy文档描述了一些选项:https://factoryboy.readthedocs.io/en/latest/orms.html#managing-sessions

工厂似乎在SQLAlchemy会话中创建对象,但该会话未写入数据库。 当您的代码进入烧瓶端时,该上下文可能正在使用与数据库的另一连接(请参见引用的flask-testing文档),因此看不到尚未提交的对象。