在python nosetests

时间:2016-04-19 21:31:04

标签: python mocking nose

我正在编写通过Flask下的路由调用的代码的测试用例。我不想通过设置测试应用程序并调用匹配路由的URL来测试代码,我想直接调用该函数。为了完成这项工作,我需要模拟flask.request,我似乎无法管理它。谷歌/ stackoverflow搜索引出了很多答案,显示如何设置测试应用程序,这不再是我想要做的。

代码会看到这个列表。

somefile.py
-----------
from flask import request

def method_called_from_route():
    data = request.values

    # do something with data here

test_somefile.py
----------------
import unittest
import somefile

class SomefileTestCase(unittest.TestCase):

    @patch('somefile.request')
    def test_method_called_from_route(self, mock_request):
       # want to mock the request.values here

我有两个问题。

(1)如上所述,修补请求不起作用。我得到类似于“AttributeError:'Blueprint'对象没有属性'somefile'”的错误

(2)如果我可以修补它,我不知道如何完全模拟请求对象。它实际上没有return_value,因为它不是函数。

我再也找不到任何关于如何做到这一点的例子,所以我觉得一个新问题是可以接受的。

4 个答案:

答案 0 :(得分:0)

尝试

test_somefile.py

import unittest
import somefile
import mock

class SomefileTestCase(unittest.TestCase):

    def test_method_called_from_route(self):
        m = mock.MagicMock()
        m.values = "MyData"
        with mock.patch("somefile.request", m):
            somefile.method_called_from_route()

unittest.main()

somefile.py

from flask import request

def method_called_from_route():
    data = request.values
    assert(data == "MyData")

这将模拟整个请求对象。 如果您只想模拟request.values,同时保持其他所有属性不变,则此方法将无效。

答案 1 :(得分:0)

这是我如何处理它的一个示例:

test_common.py模块

import pytest
import flask

def test_user_name(mocker):
    # GIVEN: user is provided in the request.headers
    given_user_name = "Some_User"
    request_mock = mocker.patch.object(flask, "request")
    request_mock.headers.get.return_value = given_user_name

    # WHEN: request.header.get method is called
    result = common.user_name()

    # THEN: user name should be returned
    request_mock.headers.get.assert_called_once_with("USERNAME", "Invalid User")
    assert result == given_user_name

common.py模块

import flask

def user_name():
    return flask.request.headers.get("USERNAME", "Invalid User")

答案 2 :(得分:0)

在提出问题几年后,这就是我用 python 3.9 解决这个问题的方法(其他提议的解决方案停止与 python 3.8 一起使用,请参阅here)。我正在使用 pytestpytest-mock,但只要您在一定程度上使用本机 unittest.mock.patch(pytest-mock 本质上只是包装这些方法在一个更易于使用的 api 中)。不幸的是,它确实需要您设置一个测试应用程序,但是您不需要经过使用 test_client 的过程,直接调用该函数即可。

这可以通过使用 Application Factory Design Pattern 和注入应用程序配置轻松处理。然后,只需使用创建的应用程序的 .test_request_context 作为上下文管理器来模拟请求对象。使用 .test_request_context 作为上下文管理器,使上下文中调用的所有内容都可以访问请求对象。下面是一个例子。

import pytest

from app import create_app

@pytest.fixture
def request_context():
    """create the app and return the request context as a fixture
       so that this process does not need to be repeated in each test
    """
    app = create_app('module.with.TestingConfig')
    return app.test_request_context

def test_something_that_requires_global_request_object(mocker, request_context):
    """do the test thing"""
    with request_context():
        # mocker.patch is just pytest-mock's way of using unittest.mock.patch
        mock_request = mocker.patch('path.to.where.request.is.used')
        # make your mocks and stubs
        mock_request.headers = {'content-type': 'application/json'}
        mock_request.get_json.return_value = {'some': 'json'}
   
    # now you can do whatever you need, using mock_request, and you do not
    # need to remain within the request_context context manager
    run_the_function()
    mock_request.get_json.assert_called_once()
    assert 1 == 1
    # etc.

pytest 很棒,因为它允许您轻松地为您的测试设置装置,如上所述,但您可以使用 UnitTest 的 setUp 实例方法做本质上相同的事情。如有必要,很乐意为应用程序工厂设计模式提供示例或更多上下文!

答案 3 :(得分:-2)

你要做的是适得其反。 RFC 2616之后的请求是:

  

从客户端到服务器的请求消息在该消息的第一行中包括要应用于资源的方法,资源的标识符以及正在使用的协议版本。

模拟Flask请求你需要重建它的结构,当然,你不会想做!

最好的方法应该是使用像Flask-Testing之类的东西,或者使用this之类的食谱,然后测试你的方法。