简体   繁体   中英

Usage of app_context when testing with Flask-SQLAlchemy

I'm moving from using just Flask and SQLAlchemy with own setup for the database connection with engine and session to using Flask-SQLAlchemy . I have a lot of tests that look something like this:

def setUp(self):
    self.app = create_app()
    self.client = self.app.test_client()

def test_put_example(self):
    assert len(Example.query.all()) == 0

    data = [{
        "id": 1,
        "valid": True,
    }, {
        "id": 2,
        "valid": True,
    }, {
        "id": 3,
        "valid": True,
    }, {
        "id": 4,
        "valid": True,
    }, {
        "id": 5,
        "valid": False,  # Lets assume this value is illegal in some way and will not be accepted
    }]

    response = self.client.open('/v1/example', method='PUT', data=json.dumps(data))
    json_value = json.loads(response.data)
    assert not json_value['success'] and json_value['message'] == 'Unauthorized'

    assert len(Example.query.all()) == 0

Here I assume 0 entries in the database prior to testing the endpoint and 0 entries after testing the endpoint. When using Flask and SQLAlchemy separately this worked fine as the transaction was implicitly rolled back at the end of the request if there was no commit during the request.

After moving to Flask-SQLAlchemy I'm having trouble finding a good way to perform this kind of test. I was hoping to not have to mess too much with the individual tests, however if I alter my setUp for example like this:

def setUp(self):
    self.app = create_app()
    self.app.app_context().push()
    self.client = self.app.test_client()

Then the last check for assert len(Example.query.all()) == 0 fails, as it apparently didn't roll back the elements that were valid, so I get 4 as the length.

It should be noted that when running the app I do appear to get the roll back that I desire, just not during testing.

I see that I could potentially do with self.app.app_context() before each of the Example.query -calls, however that would mean many changes to individual tests, so I'd prefer to not have to do that, as well as not being entirely confident in my understanding of how to properly use the AppContext .

Is there any way I can properly provide an AppContext for my queries pre and post the endpoint testing without altering individual tests and still make the test_client do the roll back?

I'm not sure this is an ideal approach but what I ended up doing so far was this:

I no longer keep the test_client around, but rather wrap calls to it to initialize on demand with its own app_context . I do push a app_context which will be used by the queries I run.

def setUp(self):
    self.app = create_app()
    self.context = self.app.app_context()
    self.context.push()

def tearDown(self):
    self.context.pop()

def client_open(self, url, method, data):
    with self.app.app_context():
        with self.app.test_client() as client:
            result = client.open(url, method=method, data=json.dumps(data))

def test_put_example(self):
    assert len(Example.query.all()) == 0

    data = [{
        "id": 1,
        "valid": True,
    }, {
        "id": 2,
        "valid": True,
    }, {
        "id": 3,
        "valid": True,
    }, {
        "id": 4,
        "valid": True,
    }, {
        "id": 5,
        "valid": False,  # Lets assume this value is illegal in some way and will not be accepted
    }]

    response = client_open('/v1/example', 'PUT', json.dumps(data))  # Wrapped call to test_client using own app_context
    json_value = json.loads(response.data)
    assert not json_value['success'] and json_value['message'] == 'Unauthorized'

    assert len(Example.query.all()) == 0

This way the app_context used by the test_client is torn down after use, and the app_context used by the queries is available throughout the test.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM