简体   繁体   English

为什么模拟补丁仅在运行特定测试而不是整个测试套件时才有效?

[英]Why does mock patch only work when running specific test and not whole test suite?

I'm using Django and Pytest specifically to run the test suite and am trying to test that a specific form shows up with expected data when a user hits the site (integration test).我正在使用 Django 和 Pytest 专门运行测试套件,并尝试测试当用户访问站点时特定表单是否显示预期数据(集成测试)。

This particular view uses a stored procedure, which I am mocking since the test would never have access to that.此特定视图使用存储过程,我是 mocking,因为测试永远无法访问它。

My test code looks like this:我的测试代码如下所示:

#test_integrations.py

from my_app.tests.data_setup import setup_data, setup_sb7_data
from unittest.mock import patch

...

# Setup to use a non-headless browser so we can see whats happening for debugging
@pytest.mark.usefixtures("standard_browser")
class SeniorPageTestCase(StaticLiveServerTestCase):
    """
    These tests surround the senior form
    """

    @classmethod
    def setUpClass(cls):
        cls.host = socket.gethostbyname(socket.gethostname())
        super(SeniorPageTestCase, cls).setUpClass()

    def setUp(self):
        # setup the dummy data - this works fine
        basic_setup(self)
        # setup the 'results'
        self.sb7_mock_data = setup_sb7_data(self)

    @patch("my_app.utils.get_employee_sb7_data")
    def test_senior_form_displays(self, mock_sb7_get):
        # login the dummy user we created
        login_user(self, "futureuser")
        # setup the results
        mock_sb7_get.return_value = self.sb7_mock_data
        # hit the page for the form
        self.browser.get(self.live_server_url + "/my_app/senior")
        form_id = "SeniorForm"
        # assert that the form displays on the page
        self.assertTrue(self.browser.find_element_by_id(form_id))
# utils.py

from django.conf import settings
from django.db import connections


def get_employee_sb7_data(db_name, user_number, window):
    """
    Executes the stored procedure for getting employee data

    Args:
        user_number: Takes the user_number
        db (db connection): Takes a string of the DB to connect to

    Returns:

    """
    cursor = connections[db_name].cursor()
    cursor.execute(
        'exec sp_sb7 %s, "%s"' % (user_number, window.senior_close)
    )
    columns = [col[0] for col in cursor.description]
    results = [dict(zip(columns, row)) for row in cursor.fetchall()]
    return results
# views.py

from myapp.utils import (
    get_employee_sb7_data,
)

...

###### Senior ######
@login_required
@group_required("user_senior")
def senior(request):

    # Additional Logic / Getting Other Models here

    # Execute stored procedure to get data for user
    user_number = request.user.user_no
    results = get_employee_sb7_data("production_db", user_number, window)
    if not results:
        return render(request, "users/senior_not_required.html")

    # Additional view stuff

    return render(
        request,
        "users/senior.html",
        {
            "data": data,
            "form": form,
            "results": results,
        },
    )

If I run this test itself with:如果我自己运行这个测试:

pytest my_app/tests/test_integrations.py::SeniorPageTestCase

The tests pass without issue.测试通过没有问题。 The browser shows up - the form shows up with the dummy data as we would expect and it all works.浏览器出现了——表单显示了我们所期望的虚拟数据,并且一切正常。

However, if I run:但是,如果我运行:

pytest my_app

All other tests run and pass - but all the tests in this class fail because it's not patching the function.所有其他测试都运行并通过 - 但此 class 中的所有测试都失败了,因为它没有修补 function。

It tries to call the actual stored procedure (which fails because it's not on the production server yet) and it fails.它尝试调用实际的存储过程(由于它不在生产服务器上而失败)并且它失败了。

Why would it patch correctly when I call that TestCase specifically - but not patch correctly when I just run pytest on the app or project level?为什么当我专门调用该 TestCase 时它会正确修补 - 但当我只是在应用程序或项目级别运行pytest时无法正确修补?

I'm at a loss and not sure how to debug this very well.我很茫然,不知道如何很好地调试它。 Any help is appreciated任何帮助表示赞赏

So what's happening is that your views are imported before you're patching.所以发生的情况是,您的视图是在修补之前导入的。

Let's first see the working case:让我们先看看工作案例:

  1. pytest imports the test_integrations file pytest 导入 test_integrations 文件
  2. the test is executed and patch decorator's inner function is run执行测试并运行补丁装饰器的内部 function
  3. there is no import of the utils yet and so patch imports and replaces the function还没有导入实用程序,因此补丁导入并替换了 function
  4. test body is executed, which passes a url to the test client执行测试体,将 url 传递给测试客户端
  5. the test client imports the resolver and in turn it imports the views, which imports the utils.测试客户端导入解析器,然后导入视图,视图导入实用程序。
  6. Since the utils are already patched, everything works fine由于 utils 已经打补丁,一切正常

If another test case runs first, that also imports the same views, then that import wins and patch cannot replace the import.如果另一个测试用例首先运行,它也导入相同的视图,那么该导入获胜并且补丁不能替换导入。

Your solution is to reference the same symbol.您的解决方案是引用相同的符号。 So in test_integrations.py :所以在test_integrations.py

@patch("myapp.views.get_employee_sb7_data")

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

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