简体   繁体   English

使用 Motor AsyncIO 和 Pytest 测试 MongoDB 功能

[英]Testing for MongoDB Functionality using Motor AsyncIO and Pytest

So I am trying to write several tests to test my functions that use an async MongoDB connection.所以我正在尝试编写几个测试来测试我使用异步 MongoDB 连接的函数。 To connect to MongoDB I use Motor with asyncio.为了连接到 MongoDB,我使用 Motor 和 asyncio。 I need help with mocking the Motor connection.我需要帮助模拟电机连接。

My Code:我的代码:

commons.py公地.py

mongo = None

blacklist.py黑名单.py

import commons

class Blacklist(object):
    async def check_if_blacklisted(self, word: str):
        blacklisted = False
        if await commons.mongo.dbtest.blacklist.find_one({'word': word}):
            blacklisted = True
        return blacklisted

main.py主文件

import asyncio
from blacklist import Blacklist
from motor.motor_asyncio import AsyncIOMotorClient
import commons

async def run():
    commons.mongo = AsyncIOMotorClient("mongodb://localhost", io_loop=asyncio.get_event_loop())
    blacklist_checker = Blacklist()
    result = await blacklist_checker.check_if_blacklisted(word="should_be_false")
    print(result)
    # > False

    result = await blacklist_checker.check_if_blacklisted(word="should_be_true")
    print(result)
    # > True

loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()

I now want to test blacklist.py by mocking the Motor Connection but I cannot seem to get the test running properly.我现在想通过模拟电机连接来测试 blacklist.py,但我似乎无法正常运行测试。 Here are the codes that I've tried:以下是我尝试过的代码:

test_blacklist.py test_blacklist.py

import pytest
from blacklist import Blacklist

class TestBlacklist(object):

@pytest.fixture
async def motor(self, event_loop):
    # I know I'm not mocking the Motor Connection here, 
    # but just wanted to show you the output using this fixture.
    commons.mongo = motor.motor_asyncio.AsyncIOMotorClient(io_loop=event_loop)
    yield commons.mongo
    commons.mongo.close()

@pytest.mark.asyncio
async def test_check_if_blacklisted(self):
    blacklist_checker = Blacklist()
    blacklisted = await blacklist_checker.check_if_blacklisted(word="should_be_false")
    assert blacklisted == False
    # > AttributeError: 'NoneType' object has no attribute 'blacklist'

pytest-mongodb: pytest-mongodb:

import pytest
from unittest.mock import patch
from blacklist import Blacklist

class TestBlacklist(object):

    @pytest.mark.asyncio
    async def test_check_if_blacklisted(self, mongodb):
        with patch("blacklist.commons.mongo") as db:
            db = mongodb
            blacklist_checker = Blacklist()
            blacklisted = await blacklist_checker.check_if_blacklisted(word="should_be_false")
        assert blacklisted == False
        # > TypeError: object MagicMock can't be used in 'await' expression

I tried searching online but I could not find a proper thread which would help me to perform the test while mocking the Motor connection which is async.我尝试在线搜索,但找不到合适的线程来帮助我在模拟异步电机连接时执行测试。 Moreover, if you think that the direction I'm heading into for testing isn't right, kindly let me know since I am new to writing tests, especially with async db connections.此外,如果您认为我的测试方向不正确,请告诉我,因为我是编写测试的新手,尤其是异步数据库连接。

Note: blacklist.py has various functions that require MongoDB functionality so it would be great if in my test_blacklist.py I could just initialize commons.mongo once and all the subsequent tests use that.注意: blacklist.py 有各种需要 MongoDB 功能的函数,所以如果在我的 test_blacklist.py 中我可以只初始化 commons.mongo 一次并且所有后续测试都使用它,那就太好了。

You can mock the async MongoDB database with pytest-async-mongodb but have in mind that it's outdated and has dependency errors so you have to fix the dependencies versions as followings:您可以使用pytest-async-mongodb模拟异步 MongoDB 数据库,但请记住,它已过时且存在依赖项错误,因此您必须按如下方式修复依赖项版本:

mongomock==3.12.0
pyyaml==3.13
pytest-asyncio==0.10.0
pytest==3.6.4

With pytest-async-mongodb you can get the mocked DB in the test by adding an argument called async_mongodb .使用pytest-async-mongodb,您可以通过添加一个名为async_mongodb的参数来获取测试中的模拟数据库 I'm going to let you the code and the structure.我会让你的代码和结构。

project
  -app
    __init__.py
    blacklist.py
    commons.py
  -test
    -fixtures
      blacklist.json
    __init__.py
    test_blacklist.py
  main.py
  pytest.ini

main.py主文件

import asyncio
from app.blacklist import Blacklist
from app.commons import get_database, set_client
from motor.motor_asyncio import AsyncIOMotorClient


async def run():
    set_client(
        AsyncIOMotorClient("mongodb://localhost", io_loop=asyncio.get_event_loop())
    )
    db = await get_database()
    blacklist_checker = Blacklist()
    result = await blacklist_checker.check_if_blacklisted(db, word="should_be_false")
    print(result)
    # > False

    result = await blacklist_checker.check_if_blacklisted(db, word="should_be_true")
    print(result)
    # > True


loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()

blacklist.py黑名单.py

from motor.motor_asyncio import AsyncIOMotorDatabase


class Blacklist(object):
    async def check_if_blacklisted(self, db: AsyncIOMotorDatabase, word: str):
        blacklisted = False
        if await db.blacklist.find_one({"word": word}):
            blacklisted = True
        return blacklisted

commons.py公地.py

from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase


class DataBase:
    client: AsyncIOMotorClient = None


db = DataBase()


async def get_database() -> AsyncIOMotorDatabase:
    return db.client["dbtest"]


def set_client(client):
    db.client = client

test_blacklist.py test_blacklist.py

import pytest
from app import blacklist


@pytest.mark.asyncio
async def test_should_be_false(async_mongodb):
    blacklist_checker = blacklist.Blacklist()
    blacklisted = await blacklist_checker.check_if_blacklisted(
        async_mongodb, word="should_be_false"
    )
    assert blacklisted == False


@pytest.mark.asyncio
async def test_should_be_true(async_mongodb):
    blacklist_checker = blacklist.Blacklist()
    blacklisted = await blacklist_checker.check_if_blacklisted(
        async_mongodb, word="should_be_true"
    )
    assert blacklisted == True

pytest.ini pytest.ini

[pytest]
async_mongodb_fixture_dir =
  test/fixtures

async_mongodb_fixtures =
  blacklist

blacklist.json黑名单.json

[
    {
      "_id": {"$oid": "60511d158f80a8d34986e2b0"},
      "word" : "should_be_true"
    }
]

The fixture can be a .yaml too and can define the amount that you want.夹具也可以是.yaml并且可以定义您想要的数量。 Read the package documentation for more information.阅读包文档以获取更多信息。

Since it is outdated, I created a fork to update it and improve it with new features.由于它已经过时,我创建了一个fork来更新它并使用新功能对其进行改进。 You are invited to take a look at it and use it if you wish.我们邀请您看一看它,如果您愿意,可以使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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