我正在为我的IHM使用PyQt5。这是一种形式。我需要用数据库中的数据填充一些组合框。
目前,它在类名Ui_dialog中。我有两个问题。
如何正确地对这些功能进行单元测试?我有时真的不知道如何开始测试。
我是否需要使用项目的资源(数据库,xml等),或者需要为测试做一些特殊数据以涵盖所有可能的情况?
def fill_combobox_from_database(self,name_of_table,name_of_field,combobox):
connexion = sqlite3.connect(os.path.dirname(os.path.abspath(__file__))+"\\"+Ui_dialog.NAME_DB)
cursor = connexion.cursor()
try:
request = "select {0} from {1}".format(name_of_field,name_of_table)
results = cursor.execute(request)
for row in results:
combobox.addItem(row[0])
except Exception as e:
print(e)
答案 0 :(得分:0)
首先,您应该将光标创建移动到应该对其进行测试的单独功能(在我命名为get_cursor
的下方)。
然后,您应该模拟execute
方法。我建议使用pytest和pytest-mock。
def test_fill_combobox_from_database_calls_execute(mocker):
mocked = mocker.patch("my_module.get_cursor")
TABLE = "foo"
FIELD = "bar"
mocked_combo = mocker.MagicMock()
REQUEST = "select {0} from {1}".format(FIELD, TABLE)
MyApp().fill_combobox_from_database(TABLE, FIELD, mocked_combo)
mocked.return_value.execute.assert_called_once()
mocked.return_value.execute.assert_called_with(REQUEST)
之后,您应该检查是否使用正确的参数调用了组合框的additem方法:
def test_fill_combobox_from_database_calls_combobox_additem(mocker):
SAMPLE = [("foo",), ("bar",), ("foobar",)]
mocked_cursor = mocker.patch("my_module.get_cursor")
mocked_cursor.return_value.execute.return_value = SAMPLE
TABLE = "foo"
FIELD = "bar"
mocked_combo = mocker.MagicMock()
REQUEST = "select {0} from {1}".format(FIELD, TABLE)
MyApp().fill_combobox_from_database(TABLE, FIELD, mocked_combo)
for item in SAMPLE:
calls = [mocker.call(item[0])]
mocked_combo.addItem.assert_has_calls(calls, any_order=True)
,您还应该测试异常:
def test_fill_combobox_from_database_on_exception(mocker):
mocked_exception = Exception()
mocker.patch("my_module.get_cursor")
mocked_cursor.return_value.execute.side_effect = mocked_exception
mocked = mocker.patch("my_module.print")
MyApp().fill_combobox_from_database("foo", "bar", mocker.MagicMock())
mocked.assert_called_once()
mocked.assert_called_with(mocked_exception)
答案 1 :(得分:0)
首先是的,如果您想构建一个真正的功能测试,则应该构建一些东西来测试您要测试的功能的全部性能。它应该包括好数据,边界数据,坏数据和/或基本上任何您认为可能从其来源接收到的东西。
在使用GUI时,我总是这样做,但是首先您必须记住,在MVC方法论(即您应该使用的方式)中,前端通过a与后端完全分离调用中间层,因此,您要做的就是创建一个虚拟函数,该函数模拟您从计划调用的中间层调用中获得的收益。
因此,您的函数应包括调用简单地进行调用的函数,例如
def fill_combobox(self,combobox):
results = GetThisRecSet(parameter(s))
for row in results:
combobox.addItem(row[0])
没有任何对SQL的引用或任何与后端集成的引用。现在,参数可能是您需要的FieldName或FieldNames列表,但它们甚至不必是在数据库中找到的FieldName,因此您绝对不应引用表。前端(如上所述)应该基本上完全不了解后端,它只是发送想要的内容,中间层将其转换并从后端获取任何内容,然后以格式将其提供给前端不管该格式是什么,都可以期待。
通常,我传递一个记录集,这就足够了,但是也许您不想这样做,而是想将其他内容传递给GUI。无论这清楚地定义了什么,然后相应地创建您的虚拟函数。然后,当需要进行连接时,您只需更改虚拟函数以从实际源中获取数据,然后将该数据转换为要传递给GUI的格式。通过提供您希望GUI使用的数据,这也可以用作功能测试,这样您就可以确切地知道它得到了什么,并且只是一个即插即用的问题-取消真正的Class并替换功能测试类,因为它们都有相同的钩子集,只是测试一个使用静态值或半静态值,或者需要确定确定的内容,然后再使用该类型函数一次又一次地检查更改时是否损坏了任何内容被制作到前端。
例如(如上所述),我经常传递记录集(在适用时),因为这是我可以创建的一个虚拟类函数来实现这一目的。
class MyDatabase:
# This will handle everything pertinent to the Database
def __init__(self,pathfilename):
self.__dbasePathName = pathfilename
def GetData(self):
DataRecSet = [
{'Field1':'Value1-1', 'Field2':'Value1-2'} ,
{'Field1':'Value2-1', 'Field2':'Value2-2'} ,
{'Field1':'Value3-1', 'Field2':'Value3-2'}
]
return DataRecSet
然后,我要做的就是像往常一样实例化MyDatabase并调用这些虚拟函数,直到准备将其插入实际的后端为止。相反,我可以从中间层以类似方式测试后端,而不必担心该数据将如何显示。
最后一点,我倾向于在构建过程中构建这些功能测试类,因为我需要能够在构建它时测试GUI的各个部分,然后在完成后将其清理一点并保存作为我实际的功能测试课程。