简体   繁体   English

Flask 单元测试——模拟 Aerospike DB

[英]Flask unit tests -- mocking up Aerospike DB

I have the following endpoint,我有以下端点,

    @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

I'm trying to figure out how to unit test this (we need to get coverage on our code).我试图弄清楚如何对此进行单元测试(我们需要覆盖我们的代码)。 Do I just mock up app.db?我只是模拟 app.db 吗? How can I do that?我怎样才能做到这一点?

Any suggestions would be appreciated.任何建议,将不胜感激。

It is not uncommon to mock database calls for unit testing using something like unittest.mock and then run Aerospike in a container or VM for end-to-end testing.使用诸如unittest.mock 之类的东西模拟数据库调用进行单元测试,然后在容器或 VM 中运行 Aerospike 进行端到端测试并不少见。

However, keep in mind that the Aerospike Python client library is written in C for better performance and thus it is not easy to do partial patching (aka "monkey patching").但是,请记住,Aerospike Python 客户端库是用 C 编写的以获得更好的性能,因此进行部分修补(又名“猴子修补”)并不容易。 For example, you will get a TypeError: can't set attributes of built-in/extension type if you try to simply patch out aerospike.Client.put .例如,如果您尝试简单地修补aerospike.Client.put ,则会收到TypeError: can't set attributes of built-in/extension type

One approach is to create a mock client object to replace or sub-class the Aerospike client object.一种方法是创建一个模拟客户端对象来替换 Aerospike 客户端对象或对其进行子类化。 The implementation of this mock object depends on your code and the cases you are testing for.这个模拟对象的实现取决于你的代码和你正在测试的案例。

Take the following example code in which app.db is an instance of the Aerospike client library:以下面的示例代码为例,其中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)

In writing unit tests for the upload_data function let's assume you want to test for a success case which is determined to mean that the put method is called and no exceptions are raised:在为upload_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()

In the test_upload_data_success method the App.db property is patched with the MockClient class instead of the aerospike.Client class.test_upload_data_success方法App.db属性修补与MockClient类代替aerospike.Client类。 The put method of the MockClient instance is also patched so that it can be asserted that the put method gets called after upload_data is called. MockClient实例的put方法也被修补,以便可以断言在调用upload_data之后调用put方法。

To test that an exception raised by the Aerospike client is re-raised from the upload_data function, the MockClient class can be modified to raise an exception explicitly:要测试 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()

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

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