1

Context

I'm creating Flask app connected to mongodb using MongoEngine via flask-mongoengine extension. I create my app using application factory pattern as specified in configuration instructions.

Problem

While running test(s), I specified testing database named datazilla_test which is passed to mongo instance via mongo.init_app(app). Even though my app.config['MONGODB_DB'] and mongo.app.config['MONGODB_DB'] instance has correct value (datazilla_test), this value is not reflected in mongo instance. Thus, when I run assertion assert mongo.get_db().name == mongo.app.config['MONGODB_DB'] this error is triggered AssertionError: assert 'datazzilla' == 'datazzilla_test'

Question

What am I doing wrong? Why database connection persist with default database datazzilla rather, than datazilla_test? How to fix it?

Source Code

# __init__.py
from flask_mongoengine import MongoEngine

mongo = MongoEngine()


def create_app(config=None):
    app = Flask(__name__)

    app.config['MONGODB_HOST'] = 'localhost'
    app.config['MONGODB_PORT'] = '27017'
    app.config['MONGODB_DB'] = 'datazzilla'

    # override default config
    if config is not None:
        app.config.from_mapping(config)

    mongo.init_app(app)

    return app
# conftest.py
import pytest
from app import mongo
from app import create_app


@pytest.fixture
def app():
    app = create_app({
        'MONGODB_DB': 'datazzilla_test',
    })

    assert mongo.get_db().name == mongo.app.config['MONGODB_DB']
    # AssertionError: assert 'datazzilla' == 'datazzilla_test'

    return app

bagerard
  • 5,681
  • 3
  • 24
  • 48
Lukasz Dynowski
  • 11,169
  • 9
  • 81
  • 124
  • It sounds like your global `mongo` instance is already connected to the "datazzilla" database before your fixture gets called. Could you call `mongo.get_db()` before the `create_app` call and let me know the outcome (if no other connection got made, it should raise a MongoEngineConnectionError) – bagerard Jun 03 '19 at 20:13
  • I did what you suggested. The outcome of calling `mongo.get_db()` before `create_app` results with db connection pointed to `datazzilla` db. – Lukasz Dynowski Jun 04 '19 at 12:02

2 Answers2

2

Mongoengine is already connected when your fixture is called, when you call app = create_app from your fixture, it tries to re-establish the connection but fails silently (as it sees that there is an existing default connection established).

This got reworked in the development version of mongoengine (see https://github.com/MongoEngine/mongoengine/pull/2038) but wasn't released yet (As of 04-JUN-2019). When that version gets out, you'll be able to disconnect any existing mongoengine connection by calling disconnect_all

In the meantime, you can either: - check where the existing connection is created and prevent it - try to disconnect the existing connection by using the following:

from mongoengine.connection import disconnect, _connection_settings

@pytest.fixture
def app():
    disconnect()
    del _connection_settings['default']
    app = create_app(...)
    ...

But it may have other side effects

Lukasz Dynowski
  • 11,169
  • 9
  • 81
  • 124
bagerard
  • 5,681
  • 3
  • 24
  • 48
  • Thanks, it worked! So far I cannot experience any side effects therfore, I'm up-voting this question :) I'm looking forward to see mongoengine release > 0.17.0 and test `disconnect_all`. P.S. My mongoclient throws this warning `MongoClient opened before fork. Create MongoClient only after forking.` By any chance are you aware of this behavior and know how to fix it ? – Lukasz Dynowski Jun 06 '19 at 08:41
  • 1
    P.S.2 I found solution for `UserWarning: MongoClient opened before fork.` I had to change connect property on mongo client from `true` (default) to `false`. e.g `MongoClient(connect=False)` – Lukasz Dynowski Jun 06 '19 at 08:58
  • It sounds like you are forking the process after it established the Mongoengine connection – bagerard Jun 06 '19 at 22:36
0

Context

Coincidentally, I figure-out fix for this problem. @bagerard answer is correct! It works for MongoClient where client's connect is set to True -this is/should be default value.

MongoClient(host=['mongo:27017'], document_class=dict, tz_aware=False, connect=False, read_preference=Primary())

If that is the case, then you have to disconnect database and delete connection settings as @bagerard explains.

Solution

However, if you change MongoClient connection to False, then you don't have to disconnect database and delete connection settings. At the end solution that worked for me was this solution.

def create_app(config=None):
    ...
    app.config['MONGODB_CONNECT'] = False
    ...

Notes

As I wrote earlier. I found this solution coincidentally, I was trying to solve this problem MongoClient opened before fork. Create MongoClient only after forking. It turned out that it fixes both problems :)

P.S If there are any side effects I'm not aware of them at this point! If you find some then please share them in comments section.

Lukasz Dynowski
  • 11,169
  • 9
  • 81
  • 124