使用伪造的mongoDB进行pytest测试 [英] Using a fake mongoDB for pytest testing

查看:308
本文介绍了使用伪造的mongoDB进行pytest测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有连接到MongoDB客户端的代码,我正在尝试对其进行测试.对于测试,我不想连接到实际的客户端,因此我试图找出一个用于测试目的的假冒产品.代码的基本流程是,我在某处有一个函数,该函数创建一个pymongo客户端,然后查询该客户端并做出一个在其他地方使用的字典.

I have code that connects to a MongoDB Client and I'm trying to test it. For testing, I don't want to connect to the actual client, so I'm trying to figure out make a fake one for testing purposes. The basic flow of the code is I have a function somewhere the creates a pymongo client, then queries that and makes a dict that is used elsewhere.

我想使用pytest编写一些测试,这些测试将测试将调用get_stuff的不同函数和类.我的问题是get_stuff会调用mongo(),这实际上是与数据库建立连接的原因.我试图只用pytest.fixture(autouse=True)mongomock.MongoClient()代替mongo().

I want to write some tests using pytest that will test different functions and classes that will call get_stuff. My problem is that get_stuff calls mongo() which is what actually makes the connection to the database. I was trying to just use pytest.fixture(autouse=True) and mongomock.MongoClient() to replace mongo().

但这不能代替mongo_stuff.mongo().有什么办法可以告诉pytest替换函数,以便调用我的fixture而不是实际的函数?我以为制作fixture会使我的测试mongo()在名称空间中的优先级高于实际模块中的功能.

But this isn't replacing the mongo_stuff.mongo(). Is there some way I can tell pytest to replace a function so my fixture is called instead of the actual function? I thought making the fixture would put my testing mongo() higher priority in the namespace than the function in the actual module.

这是我的示例的示例文件结构:

Here is an example file structure with my example:

.
├── project
│   ├── __init__.py
│   ├── mongo_stuff
│   │   ├── __init__.py
│   │   └── mongo_stuff.py
│   └── working_class
│       ├── __init__.py
│       └── somewhere_else.py
└── testing
    ├── __init__.py
    └── test_stuff.py

mongo_stuff.py

import pymongo

def mongo():
    return pymongo.MongoClient(connection_params)

def get_stuff():
    db = mongo()  # Makes the connection using another function
    stuff = query_function(db)  # Does the query and makes a dict
    return result

somewhere_else.py

from project.mongo_stuff import mongo_stuff

mongo_dict = mongo_stuff.get_stuff()

test_stuff.py

import pytest
import mongomock

@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
    db = mongomock.MongoClient()
    def fake_mongo():
        return db
    monkeypatch.setattr('project.mongo_stuff.mongo', fake_mongo)

from poject.working_class import working_class  # This starts by calling project.mongo_stuff.mongo_stuff.get_stuff()

这当前会给我一个连接错误,因为 mongo_stuff.py 中的connection params仅在生产环境中可用.如果我将 test_stuff.py 中的import语句放入测试函数中,则它可以正常工作,并且mongomock db将在测试环境中使用.我还尝试将setattr更改为monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo),这也行不通.

And this will currently give me a connection error since the connection params in mongo_stuff.py are only made to work in the production environment. If I put the import statement from test_stuff.py into a test function, then it works fine and mongomock db will be used in the testing enviornment. I also tried change the setattr to monkeypatch.setattr('project.working_class.mongo_stuff.mongo', fake_mongo) which also does not work.

推荐答案

您已经完成了一半:您已经为db客户端创建了一个模拟,现在您必须修补mongo_stuff.mongo函数以返回该模拟,而不是返回真正的联系:

You're halfway there: you have created a mock for the db client, now you have to patch the mongo_stuff.mongo function to return the mock instead of a real connection:

@pytest.fixture(autouse=True)
def patch_mongo(monkeypatch):
    db = mongomock.MongoClient()
    def fake_mongo():
        return db
    monkeypatch.setattr('mongo_stuff.mongo', fake_mongo)

出现连接错误的原因是要在test_stuff的模块级别上导入somewhere_else,而somewhere_else也在模块级别上运行连接代码.因此,用灯具打补丁太迟了,不会有任何效果.如果要在模块级别导入,则必须在导入somewhere_else之前先修补mongo客户端 .这样可以避免出现错误,但是非常丑陋:

The reason why you get the connection error is that you are importing somewhere_else on module level in test_stuff, and somewhere_else runs connection code also on module level. So patching with fixtures will come too late and will have no effect. You have to patch the mongo client before the import of somewhere_else if you want to import on module level. This will avoid the error raise, but is extremely ugly:

from project.mongo_stuff import mongo_stuff
import mongomock
import pytest

from unittest.mock import patch

with patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient()):

    from project.working_class import somewhere_else


@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db1(mocked_mongo):
    mongo_stuff.mongo()
    assert True


@patch.object(mongo_stuff, 'mongo', return_value=mongomock.MongoClient())
def test_db2(mocked_mongo):
    somewhere_else.foo()
    assert True

您应该尽可能避免在模块级别运行代码,或者在测试内部运行在模块级别执行代码的导入(如您在注释中已经发现的那样).

You should rather avoid running code on module level when possible, or run the imports that execute code on module level inside the tests (as you already found out in the comments).

这篇关于使用伪造的mongoDB进行pytest测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆