单元测试Flask app - 在应用程序中模拟全局变量

时间:2018-02-15 14:14:57

标签: python flask python-unittest

我有一个看起来像这个

的Flask应用模块(app.py
# imports
...
from flask import Flask, request, Response
...

# module-level vars, including `logger` and `APP`
...
logger = None
APP = None
...

def init():
    """
    Initialisation of app resources, including `logger`
    """
    ...
    APP = Flask(__name__) 
    ...
    logger = logging.getLogger()
    ...
    ...

try:
    init()
except Exception as e:
    logger.error(str(e))

@APP.route('/healthcheck', methods=['GET'])
def healthcheck():
    """
    Healthcheck endpoint - just returns OK if the app
    initialised OK.
    """
    return 'OK'

@APP.route('/get_keys', method=['POST'])
def get_keys():
    """
    Main endpoint - accepts a POST request from a client
    containing either a CSV or JSON payload defining a set
    of geographic locations, and then returns some "keys"
    for these.
    """
    try:
        logger.info('Extracting payload')
        # extract payload
        logger.info('Processing for keys')
        # do some stuff
        ...
        ...
    except Exception as e:
        logger.error("Error: {}.".format(str(e)))

    # return response

我已经对AppTests子包中的模块tests中定义的Flask应用进行了单元测试。

# general imports including `unittest` etc.
# import app module as `app`

class AppTests(unittest.TestCase):
    """
    Flask app tests
    """

    @classmethod
    def setUpClass(self):

        app.APP.config['TESTING'] = True
        app.APP.config['DEBUG'] = False

        self.app = app.APP.test_client()

        # define other resources needed for `self.app`


    def test_healthcheck(self):

        res = self.app.get(path='/healthcheck')

        self.assertEqual(res.status_code, 200)

    def test_get_keys__csv(self):

        # define sample csv data in `data` variable

        headers = {
            'Accept-Encoding': 'identity,deflate,gzip,compress',
            'Content-Type': 'text/csv; charset=utf-8',
            'Content-Length': len(data)
        }

        res = self.app.post(path='/get_keys', headers=headers.items(), data=data)

        self.assertEqual(res.status_code, 200)

healthcheck端点的测试通过,但get_keys端点的测试失败。

$ python -m unittest -v AppTests.AppTests.test_get_keys__csv
test_get_keys__csv (AppTests.AppTests) ... 
ERROR

======================================================================
ERROR: test_get_keys__csv (AppTests.AppTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "AppTests.py", line 105, in test_get_keys__csv
    res = self.app.post(path='/get_keys', headers=headers.items(), data=data)
  File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 801, in post
    return self.open(*args, **kw)
  File "/path/to/venv/lib/python2.7/site-packages/flask/testing.py", line 127, in open
    follow_redirects=follow_redirects)
  File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 764, in open
    response = self.run_wsgi_app(environ, buffered=buffered)
  File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 677, in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
  File "/path/to/venv/lib/python2.7/site-packages/werkzeug/test.py", line 884, in run_wsgi_app
    app_rv = app(environ, start_response)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1994, in __call__
    return self.wsgi_app(environ, start_response)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "/path/to/venv/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/path/to/app.py", line 205, in get_keys
    logger.error("Error: {}.".format(str(e)))
AttributeError: 'NoneType' object has no attribute 'error'

----------------------------------------------------------------------
Ran 1 test in 0.036s

FAILED (errors=1)

当我调用logger时,应用程序中get_keys端点中对self.app.post('/get_keys, headers=headers.items(), data=data)对象的引用似乎为null。每次调用logger.info都会在端点中生成一个异常,该异常会被捕获并记录,这就是我在运行端点测试时所看到的。

有没有办法模拟这个,或者有些人如何绕过测试模块本身使用logger?我宁愿不修改端点方法本身。

1 个答案:

答案 0 :(得分:1)

当您运行test_get_keys__csv()时,您可能会模拟日志记录导入。

from unittest.mock import patch

@patch('path.to.app.logging')  # Mock the logging import
def test_get_keys__csv(self, mock_logging):

    # define sample csv data in `data` variable

    headers = {
        'Accept-Encoding': 'identity,deflate,gzip,compress',
        'Content-Type': 'text/csv; charset=utf-8',
        'Content-Length': len(data)
    }

    res = self.app.post(path='/get_keys', headers=headers.items(), data=data)

    self.assertEqual(res.status_code, 200)

如果你正在使用Python 2,mock是一个单独的安装。

pip install mock

然后使用

导入
from mock import patch

有关模拟的更多信息:https://docs.python.org/3/library/unittest.mock.html