【发布时间】:2020-05-07 17:26:11
【问题描述】:
我有在gui.Buttons 初始化期间创建mocker.Mock 而不是gui.Menu 对象的夹具。引用存储在Buttons.menu 属性中。在我的测试中,我检查是否在gui.Buttons.add 中调用了正确的函数。参数化测试运行良好,直到我遇到应该调用来自gui.Menu 的方法的条件。现在有一个模拟。
import pytest
from project import gui
@pytest.fixture
def buttons(mocker):
mocker.patch('project.gui.tk.Frame.__init__', return_value=None)
mocker.patch('project.gui.tk.Button')
return gui.Buttons(mocker.Mock())
@pytest.mark.parametrize('value,patched',(
(None, 'project.gui.show_no_connection'),
(False, 'project.gui.Buttons.process_data'),
(True, 'pytest.Mock.show_error'),
))
def test_add_calls_function(buttons, value, patched, mocker):
mocker.patch('project.gui.Buttons.exist_check', return_value=value)
mocked = mocker.patch(patched)
buttons.add()
mocked.assert_called_once()
对于真实对象,我可以在 @pytest.mark.parametrize 中写入 (True, 'project.gui.Menu.show_error') 而不是 (True, 'pytest.Mock.show_error'),后者不起作用并产生 ModuleNotFoundError: No module named 'pytest.Mock'。
我想知道是否可以 patch 在我的夹具中创建模拟对象以使其像其他参数化示例一样工作。甚至可能吗?如果我的理解是错误的,请纠正我。
测试代码如下:
import tkinter as tk
import tkinter.messagebox as msg
from project.connection import Database
def show_no_connection():
msg.showerror('Error', 'Could not perform operation. Try again later.')
class Menu(tk.Tk):
def __init__(self):
super().__init__()
self.form = Form()
def show_error(self, message):
self.form.clear()
msg.showerror('Error', message)
class Form(tk.Frame):
def clear(self):
print('Clearing...')
def get(self):
return {'Title': 'Test', 'ISBN': 87327837823}
class Buttons(tk.Frame):
def __init__(self, menu):
super().__init__(menu)
self.menu = menu
def process_data(self, data, operation):
operation(data)
def add(self):
data = self.menu.form.get()
exists = self.exist_check(data.get('ISBN', None))
if exists is None:
show_no_connection()
else:
if exists:
self.menu.show_error(
'Record with set ISBN already exists in database.')
else:
self.process_data(data, Database().add)
@staticmethod
def exist_check(number):
if number:
return Database().search({'ISBN': number})
return False
显示错误:
=================================== FAILURES ===================================
_________ test_add_calls_function[True-project.gui.Gui.show_error] _________
buttons = <[AttributeError("'Buttons' object has no attribute '_w'") raised in repr()] Buttons object at 0x7f840114aa10>
value = True, patched = 'project.gui.Gui.show_error'
mocker = <pytest_mock.plugin.MockFixture object at 0x7f840114ab90>
@pytest.mark.parametrize('value,patched',(
(None, 'project.gui.show_no_connection'),
(False, 'project.gui.Buttons.process_data'),
(True, 'project.gui.Gui.show_error'),
))
def test_add_calls_function(buttons, value, patched, mocker):
mocker.patch('project.gui.Buttons.exist_check', return_value=value)
mocked = mocker.patch(patched)
buttons.add()
> mocked.assert_called_once()
E AssertionError: Expected 'show_error' to have been called once. Called 0 times.
tests/test_gui_buttons.py:88: AssertionError
【问题讨论】:
-
为什么不能只使用
(True, 'project.gui.Menu.show_error')?project.gui.Menu现在指向模拟,所以它应该可以工作。 -
@MrBean-Bremen 这是我所期望的,但它不起作用。我得到
AssertionError: Expected 'show_error' to have been called once. Called 0 times. -
啊,好的 - 您在使用的测试模块中有一个
gui的本地副本。请改用import project和project.gui.Buttons。检查where to patch。 -
@MrBean-Bremen 我试过了,还是出现同样的错误。
-
@MrBean-Bremen 我添加了模仿我的代码结构的示例并粘贴了测试失败的完整信息。
标签: python testing mocking pytest