Python 烧瓶单元试验——模拟Aerospike DB

Python 烧瓶单元试验——模拟Aerospike DB,python,unit-testing,flask,aerospike,Python,Unit Testing,Flask,Aerospike,我有以下结论: @developer_blueprint.route("/init_db", methods=["POST"]) def initialize_database(): try: upload_data(current_app) logger.debug("Database entries upload.") return jsonify({"result": "Database entrie

我有以下结论:

    @developer_blueprint.route("/init_db", methods=["POST"])
    def initialize_database():
       try:
          upload_data(current_app)
          logger.debug("Database entries upload.")
          return jsonify({"result": "Database entries uploaded."}), 201
       except Exception as e:
          return jsonify({"error": str(e)})

    def upload_data(app):
       with open("src/core/data/data.json") as data_file:
          data = json.load(data_file)
          try:
             current_app.db.put(("somenamespace", "test", "default"), data, None)
          except Exception as e:
             raise e

我正试图找出如何对其进行单元测试(我们需要对代码进行覆盖)。 我只是模拟app.db吗?我该怎么做


如果您有任何建议,我们将不胜感激。

使用类似的方法模拟用于单元测试的数据库调用,然后在容器或VM中运行Aerospike进行端到端测试,这种情况并不少见

但是,请记住,Aerospike Python客户端库是用C编写的,以获得更好的性能,因此进行部分修补(也称为“猴子修补”)并不容易。例如,如果您尝试简单地修补
aerospeck.Client.put
,您将得到一个
TypeError:无法设置内置/扩展类型的属性

一种方法是创建一个模拟客户端对象来替换Aerospike客户端对象或将其子类化。此模拟对象的实现取决于您的代码和测试的案例

以以下示例代码为例,
app.db
是Aerospike客户端库的一个实例:

# example.py
import aerospike
import json

class App(object):
    db = None
    def __init__(self):
        config = {'hosts': [('127.0.0.1', 3000)]}
        self.db = aerospike.client(config).connect()

def upload_data(app):
    with open("data.json") as data_file:
        data = json.load(data_file)

    try:
        app.db.put(("ns1", "test", "default"), data, None)
    except Exception as e:
            raise e

if __name__ == "__main__":
    app = App()
    upload_data(app)
在为
upload\u data
函数编写单元测试时,假设您想要测试成功案例,该案例被确定为意味着调用了
put
方法,并且没有引发异常:

# test.py
from unittest import TestCase, main
from unittest.mock import PropertyMock, patch
from example import App, upload_data
from aerospike import Client, exception


class MockClient(Client):
    def __init__(self, *args, **kwargs):
        pass

    def put(self, *args, **kwargs):
        return 0


class ExampleTestCase(TestCase):
    def test_upload_data_success(self):
        with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
            db_mock.return_value = client = MockClient()
            app = App()
            with patch.object(client, 'put') as put_mock:
                upload_data(app)
                put_mock.assert_called()


if __name__ == '__main__':
    main()
test\u upload\u data\u success
方法中,
App.db
属性使用
MockClient
类而不是
aerospeck.Client
类进行修补。
MockClient
实例的
put
方法也进行了修补,以便可以断言调用
upload\u data
后调用了
put
方法

为了测试Aerospike客户端引发的异常是否从
upload_data
函数重新引发,可以修改
MockClient
类以明确引发异常:

# test.py
from unittest import TestCase, main
from unittest.mock import PropertyMock, patch
from example import App, upload_data
from aerospike import Client, exception


class MockClient(Client):
    def __init__(self, *args, **kwargs):
        self.put_err = None
        if 'put_err' in kwargs:
            self.put_err = kwargs['put_err']

    def put(self, *args, **kwargs):
        if self.put_err:
            raise self.put_err
        else:
            return 0


class ExampleTestCase(TestCase):
    def test_upload_data_success(self):
        with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
            db_mock.return_value = client = MockClient()
            app = App()
            with patch.object(client, 'put') as put_mock:
                upload_data(app)
                put_mock.assert_called()

    def test_upload_data_error(self):
        with patch.object(App, 'db', new_callable=PropertyMock) as db_mock:
            db_mock.return_value = MockClient(put_err=exception.AerospikeError)
            app = App()
            with self.assertRaises(exception.AerospikeError):
                upload_data(app)


if __name__ == '__main__':
    main()

是的,你会嘲笑
db
,回答如何做到这一点是一个广泛的问题,可能不适合这个网站,如果你有一些例子,你试图实现这一点,我们可能能够帮助更多这是一个伟大的答案!唯一的问题——我的团队被指示只使用Pytest而不是unittest,我应该引入unittest只是为了模拟Pytest还是在Pytest中寻找类似的函数。从我看来,这是有限的。pytest应该可以。使用monkeypatch夹具。例如,
monkeypatch.setattr(应用程序'db',MockClient())