Files
cangku/test/test_raw_material_gui.py
liangweihao 8aa1a5ac91 添加PyQt GUI自动化测试套件
新增5个GUI测试模块,覆盖所有主要功能:
- test_login_gui.py: 登录和密码管理测试(7个测试)
- test_stock_gui.py: 库存管理测试(4个测试)
- test_raw_material_gui.py: 原料管理测试(7个测试)
- test_garment_gui.py: 款式管理测试(2个测试)
- test_purchase_order_gui.py: 采购单生成测试(2个测试)

测试特点:
- 真实GUI交互测试(填写表单、点击按钮、搜索过滤)
- 业务逻辑验证(重复数据拒绝、空值验证、计算正确性)
- 独立测试环境(临时数据库,自动清理)
- 自动化消息框(Mock QMessageBox)

总计22个GUI测试,全部通过 ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 16:52:30 +08:00

279 lines
9.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
原料管理GUI测试模块
使用PyQt测试框架测试GUI交互和业务逻辑
"""
import unittest
import os
import sys
import tempfile
from PyQt5.QtWidgets import QApplication, QMessageBox, QTabWidget
from PyQt5.QtTest import QTest
from PyQt5.QtCore import Qt
# 添加父目录到路径以导入模块
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from database import DatabaseManager
from raw_material_dialog import RawMaterialLibraryDialog
class TestRawMaterialGUI(unittest.TestCase):
"""原料管理GUI测试类"""
@classmethod
def setUpClass(cls):
"""测试类初始化创建QApplication实例"""
cls.app = QApplication.instance()
if cls.app is None:
cls.app = QApplication(sys.argv)
def setUp(self):
"""每个测试前准备:创建临时数据库和对话框"""
self.temp_dir = tempfile.mkdtemp()
self.db_path = os.path.join(self.temp_dir, "test_fabric.db")
self.db_manager = DatabaseManager(self.db_path)
# 创建对话框实例(管理员模式)
self.dialog = RawMaterialLibraryDialog(self.db_path, is_admin=True)
# 禁用消息框以便自动化测试
self._original_msgbox = QMessageBox.information
self._original_warning = QMessageBox.warning
self._original_critical = QMessageBox.critical
self._original_question = QMessageBox.question
# Mock消息框
QMessageBox.information = lambda *args, **kwargs: None
QMessageBox.warning = lambda *args, **kwargs: None
QMessageBox.critical = lambda *args, **kwargs: None
QMessageBox.question = lambda *args, **kwargs: QMessageBox.Yes
def tearDown(self):
"""每个测试后清理"""
# 恢复消息框
QMessageBox.information = self._original_msgbox
QMessageBox.warning = self._original_warning
QMessageBox.critical = self._original_critical
QMessageBox.question = self._original_question
# 关闭对话框
self.dialog.close()
self.dialog.deleteLater()
# 清理数据库
import gc
gc.collect()
try:
if os.path.exists(self.db_path):
os.remove(self.db_path)
if os.path.exists(self.temp_dir):
os.rmdir(self.temp_dir)
except:
pass
# ========== 添加原料GUI测试 ==========
def test_add_raw_material_basic_gui(self):
"""测试通过GUI添加基本原料"""
# 切换到新增/编辑标签页
tabs = self.dialog.findChild(QTabWidget)
tabs.setCurrentIndex(1)
# 填写表单
self.dialog.add_major_category.setCurrentText("布料")
self.dialog.add_sub_category.setText("棉布")
self.dialog.add_model.setText("GUI-TEST-001")
self.dialog.add_supplier.setCurrentText("测试供应商")
self.dialog.add_color.setText("白色")
self.dialog.add_width.setValue(150.0)
self.dialog.add_gsm.setValue(200.0)
self.dialog.add_unit.setCurrentText("")
self.dialog.add_retail.setValue(10.0)
self.dialog.add_bulk.setValue(8.0)
# 点击保存按钮
self.dialog.save_raw_material()
# 验证数据已保存到数据库
with self.dialog.get_conn() as conn:
cursor = conn.execute("SELECT * FROM fabrics WHERE model = ?", ("GUI-TEST-001",))
row = cursor.fetchone()
self.assertIsNotNone(row, "原料应该被保存到数据库")
self.assertEqual(row[0], "GUI-TEST-001", "型号应该匹配")
def test_add_raw_material_duplicate_model_gui(self):
"""测试通过GUI添加重复型号应被拒绝"""
# 先添加一个原料
with self.dialog.get_conn() as conn:
conn.execute('''
INSERT INTO fabrics (model, category, supplier, timestamp)
VALUES (?, ?, ?, datetime('now'))
''', ("GUI-TEST-002", "布料", "供应商A"))
conn.commit()
# Mock warning消息框以捕获警告
warning_called = []
def mock_warning(*args, **kwargs):
warning_called.append(args)
QMessageBox.warning = mock_warning
# 尝试添加重复型号
tabs = self.dialog.findChild(QTabWidget)
tabs.setCurrentIndex(1)
self.dialog.add_model.setText("GUI-TEST-002")
self.dialog.add_major_category.setCurrentText("布料")
self.dialog.save_raw_material()
# 验证警告被触发
self.assertTrue(len(warning_called) > 0, "应该显示警告消息")
# 验证数据库中只有一条记录
with self.dialog.get_conn() as conn:
cursor = conn.execute("SELECT COUNT(*) FROM fabrics WHERE model = ?", ("GUI-TEST-002",))
count = cursor.fetchone()[0]
self.assertEqual(count, 1, "数据库中应该只有一条记录")
def test_add_raw_material_empty_model_gui(self):
"""测试通过GUI添加空型号应被拒绝"""
warning_called = []
def mock_warning(*args, **kwargs):
warning_called.append(args)
QMessageBox.warning = mock_warning
# 尝试保存空型号
tabs = self.dialog.findChild(QTabWidget)
tabs.setCurrentIndex(1)
self.dialog.add_model.setText("")
self.dialog.save_raw_material()
# 验证警告被触发
self.assertTrue(len(warning_called) > 0, "应该显示警告消息")
# ========== 编辑原料GUI测试 ==========
def test_edit_raw_material_gui(self):
"""测试通过GUI编辑原料"""
# 先添加一个原料
with self.dialog.get_conn() as conn:
conn.execute('''
INSERT INTO fabrics (model, category, supplier, color, timestamp)
VALUES (?, ?, ?, ?, datetime('now'))
''', ("GUI-EDIT-001", "布料", "供应商A", "白色"))
conn.commit()
# 刷新表格
self.dialog.load_table()
# 调用编辑方法
self.dialog.edit_raw_material("GUI-EDIT-001")
# 验证表单已填充
self.assertEqual(self.dialog.add_model.text(), "GUI-EDIT-001")
self.assertEqual(self.dialog.add_supplier.currentText(), "供应商A")
self.assertEqual(self.dialog.add_color.text(), "白色")
# 修改供应商
self.dialog.add_supplier.setCurrentText("供应商B")
self.dialog.save_raw_material()
# 验证修改已保存
with self.dialog.get_conn() as conn:
cursor = conn.execute("SELECT supplier FROM fabrics WHERE model = ?", ("GUI-EDIT-001",))
row = cursor.fetchone()
self.assertEqual(row[0], "供应商B", "供应商应该被更新")
# ========== 删除原料GUI测试 ==========
def test_delete_raw_material_gui(self):
"""测试通过GUI删除原料"""
# 先添加一个原料
with self.dialog.get_conn() as conn:
conn.execute('''
INSERT INTO fabrics (model, category, timestamp)
VALUES (?, ?, datetime('now'))
''', ("GUI-DEL-001", "布料"))
conn.commit()
# 刷新表格
self.dialog.load_table()
# 调用删除方法
self.dialog.delete_raw("GUI-DEL-001")
# 验证已删除
with self.dialog.get_conn() as conn:
cursor = conn.execute("SELECT * FROM fabrics WHERE model = ?", ("GUI-DEL-001",))
row = cursor.fetchone()
self.assertIsNone(row, "原料应该被删除")
# ========== 过滤和搜索GUI测试 ==========
def test_filter_by_category_gui(self):
"""测试通过GUI按类目过滤"""
# 添加测试数据
with self.dialog.get_conn() as conn:
conn.execute('''
INSERT INTO fabrics (model, category, fabric_type, timestamp)
VALUES (?, ?, ?, datetime('now'))
''', ("FILTER-001", "布料", "棉布"))
conn.execute('''
INSERT INTO fabrics (model, category, fabric_type, timestamp)
VALUES (?, ?, ?, datetime('now'))
''', ("FILTER-002", "辅料", "拉链"))
conn.commit()
# 刷新过滤器和表格
self.dialog.refresh_filters_and_table()
# 选择"布料"类目
self.dialog.major_combo.setCurrentText("布料")
# 验证表格只显示布料
row_count = self.dialog.table.rowCount()
for row in range(row_count):
category_item = self.dialog.table.item(row, 0)
if category_item:
self.assertEqual(category_item.text(), "布料", "应该只显示布料类目")
def test_search_by_model_gui(self):
"""测试通过GUI搜索型号"""
# 添加测试数据
with self.dialog.get_conn() as conn:
conn.execute('''
INSERT INTO fabrics (model, category, timestamp)
VALUES (?, ?, datetime('now'))
''', ("SEARCH-001", "布料"))
conn.execute('''
INSERT INTO fabrics (model, category, timestamp)
VALUES (?, ?, datetime('now'))
''', ("SEARCH-002", "布料"))
conn.execute('''
INSERT INTO fabrics (model, category, timestamp)
VALUES (?, ?, datetime('now'))
''', ("OTHER-001", "布料"))
conn.commit()
# 刷新表格
self.dialog.load_table()
# 输入搜索关键词
self.dialog.search_input.setText("SEARCH")
# 验证表格只显示匹配的结果
row_count = self.dialog.table.rowCount()
for row in range(row_count):
model_item = self.dialog.table.item(row, 2)
if model_item:
self.assertIn("SEARCH", model_item.text(), "应该只显示包含SEARCH的型号")
if __name__ == "__main__":
unittest.main()