6月8日

import unittest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
import logging
import os
import sys
from datetime import datetime

def setup_logger():
    """配置日志记录器"""
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)
    
    # 创建控制台处理器
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.INFO)
    
    # 创建文件处理器
    if not os.path.exists('logs'):
        os.makedirs('logs')
    file_handler = logging.FileHandler(f'logs/test_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log', encoding='utf-8')
    file_handler.setLevel(logging.INFO)
    
    # 创建格式化器
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    console_handler.setFormatter(formatter)
    file_handler.setFormatter(formatter)
    
    # 添加处理器到日志记录器
    logger.addHandler(console_handler)
    logger.addHandler(file_handler)
    
    return logger

# 配置日志
logger = setup_logger()

class TestWarehouseSystem(unittest.TestCase):
    def setUp(self):
        """设置测试环境"""
        logger.info("开始设置测试环境")
        logger.info("初始化 WebDriver")
        
        # 设置火狐浏览器路径
        firefox_path = r"C:\Program Files\Mozilla Firefox\firefox.exe"
        options = webdriver.FirefoxOptions()
        options.binary_location = firefox_path
        
        # 初始化火狐浏览器
        self.driver = webdriver.Firefox(options=options)
        self.driver.maximize_window()
        self.wait = WebDriverWait(self.driver, 10)
        
        # 登录系统
        try:
            # 访问登录页面
            logger.info("访问登录页面")
            self.driver.get('http://localhost:5000/login')
            time.sleep(2)  # 等待页面加载
            
            # 登录
            logger.info("开始登录流程")
            username_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'username')))
            password_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'password')))
            
            username_input.send_keys('admin')
            password_input.send_keys('admin')
            logger.info("点击登录按钮")
            login_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]')))
            login_button.click()
            
            # 等待登录成功
            logger.info("等待登录成功")
            time.sleep(2)
            
            # 验证登录成功
            try:
                flash_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-success')))
                self.assertIn('登录成功', flash_message.text)
                logger.info("登录成功")
            except:
                # 如果登录失败,记录错误信息
                error_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-danger')))
                logger.error(f"登录失败: {error_message.text}")
                raise Exception(f"登录失败: {error_message.text}")
            
        except Exception as e:
            logger.error(f"设置测试环境时出错: {str(e)}")
            # 确保关闭浏览器
            try:
                self.driver.quit()
            except:
                pass
            raise

    def tearDown(self):
        """清理测试环境"""
        pass  # 不再需要清理

    def test_warehouse_operations(self):
        """测试仓储管理系统的主要功能"""
        logger.info("开始测试仓储管理系统的主要功能")
        try:
            # 批量添加商品
            logger.info("开始批量添加商品")
            self.driver.get('http://localhost:5000/batch_add_products')
            time.sleep(1)
            
            # 设置添加数量为5
            count_input = self.wait.until(EC.presence_of_element_located((By.ID, 'count')))
            count_input.clear()
            count_input.send_keys('5')
            logger.info("设置添加数量为5")
            time.sleep(1)  # 等待输入完成
            
            # 使用JavaScript直接调用updateForm函数
            self.driver.execute_script("updateForm();")
            logger.info("手动触发表单更新")
            time.sleep(2)  # 等待表单更新
            
            # 填写5个商品的信息
            timestamp = int(time.time())
            for i in range(5):
                product_code = f'TEST{timestamp}_{i+1:03d}'  # 生成唯一的商品编号
                name = f'测试商品{i+1}'
                description = f'这是测试商品{i+1}的描述'
                price = f'{99.99 * (i + 1):.2f}'  # 限制价格小数点后两位
                quantity = str(100 * (i + 1))
                
                try:
                    # 获取当前商品卡片
                    card = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, f'#productsContainer .card:nth-child({i+1})')))
                    # 滚动到卡片位置
                    self.driver.execute_script("arguments[0].scrollIntoView(true);", card)
                    time.sleep(0.5)  # 等待滚动完成
                    
                    # 使用JavaScript填写表单
                    self.driver.execute_script(f"document.getElementsByName('product_code_{i}')[0].value = '{product_code}';")
                    self.driver.execute_script(f"document.getElementsByName('name_{i}')[0].value = '{name}';")
                    self.driver.execute_script(f"document.getElementsByName('description_{i}')[0].value = '{description}';")
                    self.driver.execute_script(f"document.getElementsByName('price_{i}')[0].value = '{price}';")
                    self.driver.execute_script(f"document.getElementsByName('quantity_{i}')[0].value = '{quantity}';")
                    
                    logger.info(f"填写商品 {i+1} 信息完成: 编号={product_code}, 名称={name}, 价格={price}")
                    time.sleep(1)  # 等待填写完成
                except Exception as e:
                    logger.error(f"填写商品 {i+1} 信息失败: {str(e)}")
                    raise

            # 提交表单
            logger.info("提交批量添加表单")
            # 滚动到表单底部
            self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
            time.sleep(1)  # 等待滚动完成
            
            # 找到提交按钮并确保它可见
            submit_button = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'button[type="submit"]')))
            self.driver.execute_script("arguments[0].scrollIntoView(true);", submit_button)
            time.sleep(1)  # 等待滚动完成
            
            # 使用JavaScript点击按钮
            self.driver.execute_script("arguments[0].click();", submit_button)
            logger.info("已点击提交按钮")
            time.sleep(5)  # 增加等待时间
            
            # 验证添加成功
            try:
                # 等待页面加载完成
                self.wait.until(EC.presence_of_element_located((By.TAG_NAME, 'body')))
                time.sleep(2)  # 额外等待
                
                # 检查是否有成功消息
                try:
                    success_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-success')))
                    self.assertIn('批量添加商品成功', success_message.text)
                    logger.info("批量添加商品成功")
                except:
                    # 如果没有成功消息,检查是否已经跳转到商品列表页面
                    current_url = self.driver.current_url
                    if '/products' in current_url:
                        logger.info("已跳转到商品列表页面,认为添加成功")
                    else:
                        raise Exception("未找到成功消息且未跳转到商品列表页面")
                
                time.sleep(3)  # 等待查看结果
                
                # 获取添加的商品编号
                self.driver.get('http://localhost:5000/products')
                time.sleep(3)  # 增加等待时间
                product_ids = []
                product_rows = self.driver.find_elements(By.CSS_SELECTOR, 'table tbody tr')
                for row in product_rows:
                    try:
                        product_id = row.find_element(By.CSS_SELECTOR, 'td:first-child').text
                        product_code = row.find_element(By.CSS_SELECTOR, 'td:nth-child(2)').text
                        product_name = row.find_element(By.CSS_SELECTOR, 'td:nth-child(3)').text
                        if product_code and product_name:  # 只添加有效的商品
                            product_ids.append((product_id, product_code, product_name))
                    except:
                        continue
                logger.info(f"添加的商品列表: {product_ids}")
                
            except Exception as e:
                logger.error(f"验证添加结果时出错: {str(e)}")
                raise
            
            # 修改第一个商品
            logger.info("开始修改商品")
            # 确保回到商品列表页面
            self.driver.get('http://localhost:5000/products')
            time.sleep(2)  # 等待页面加载
            
            # 找到第一个商品的编辑按钮
            edit_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'table tbody tr:first-child a.btn-primary')))
            # 滚动到按钮位置
            self.driver.execute_script("arguments[0].scrollIntoView(true);", edit_button)
            time.sleep(1)  # 等待滚动完成
            edit_button.click()
            logger.info("点击编辑按钮")
            time.sleep(2)  # 等待编辑页面加载
            
            # 修改商品信息
            logger.info("修改商品信息")
            name_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'name')))
            name_input.clear()
            name_input.send_keys('修改后的商品名称')
            time.sleep(1)  # 等待输入完成
            
            description_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'description')))
            description_input.clear()
            description_input.send_keys('这是修改后的商品描述')
            time.sleep(1)  # 等待输入完成
            
            # 修改库存数量
            quantity_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'quantity')))
            current_quantity = int(quantity_input.get_attribute('value'))
            new_quantity = current_quantity + 50  # 增加50个库存
            quantity_input.clear()
            quantity_input.send_keys(str(new_quantity))
            time.sleep(1)  # 等待输入完成
            logger.info(f"将库存数量从 {current_quantity} 修改为 {new_quantity}")
            
            # 提交修改
            logger.info("提交修改表单")
            submit_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]')))
            # 滚动到按钮位置
            self.driver.execute_script("arguments[0].scrollIntoView(true);", submit_button)
            time.sleep(1)  # 等待滚动完成
            submit_button.click()
            time.sleep(2)  # 等待提交完成
            
            # 验证商品更新成功
            success_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-success')))
            self.assertIn('更新成功', success_message.text)
            logger.info("商品更新成功")
            time.sleep(2)
            
            # 开始搜索商品
            logger.info("开始搜索商品")
            # 确保在商品列表页面
            self.driver.get('http://localhost:5000/products')
            time.sleep(2)  # 等待页面加载
            
            # 选择按名称搜索
            search_type = self.wait.until(EC.presence_of_element_located((By.NAME, 'type')))
            self.driver.execute_script("arguments[0].value = 'name';", search_type)
            logger.info("选择按名称搜索")
            time.sleep(1)  # 等待选择完成
            
            # 输入搜索关键词
            search_input = self.wait.until(EC.presence_of_element_located((By.NAME, 'query')))
            search_input.clear()
            search_input.send_keys('修改后的')
            logger.info("输入搜索关键词")
            time.sleep(1)  # 等待输入完成
            
            # 点击搜索按钮
            logger.info("点击搜索按钮")
            search_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button[type="submit"]')))
            search_button.click()
            time.sleep(2)  # 等待搜索结果
            
            # 验证搜索结果
            table = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table tbody')))
            product_name = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table tbody tr:first-child td:nth-child(3)')))
            self.assertIn('修改后的', product_name.text)
            logger.info("搜索成功,找到修改后的商品")
            time.sleep(3)  # 等待查看结果
            
            # 删除第一个商品
            logger.info("开始删除第一个商品")
            self.driver.get('http://localhost:5000/products')
            time.sleep(1)
            
            # 选择第一个商品
            first_checkbox = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table tbody tr:first-child input[type="checkbox"]')))
            first_checkbox.click()
            time.sleep(1)
            
            # 点击批量删除按钮
            delete_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button.btn-danger')))
            delete_button.click()
            time.sleep(1)
            
            # 确认删除
            confirm_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#batchDeleteModal .btn-danger')))
            confirm_button.click()
            time.sleep(1)
            
            # 验证删除成功
            success_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-success')))
            self.assertIn('批量删除商品成功', success_message.text)
            logger.info("第一个商品删除成功")
            time.sleep(1)
            
            # 删除剩余商品 - 使用表头的全选框
            logger.info("开始删除剩余商品")
            self.driver.get('http://localhost:5000/products')
            time.sleep(1)
            
            # 检查是否有商品
            table = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table tbody')))
            rows = table.find_elements(By.TAG_NAME, 'tr')
            
            if rows:
                logger.info(f"发现 {len(rows)} 个商品需要删除")
                
                # 选择表头的全选框
                header_checkbox = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'table thead tr th:first-child input[type="checkbox"]')))
                header_checkbox.click()
                time.sleep(1)
                
                # 点击批量删除按钮
                delete_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, 'button.btn-danger')))
                delete_button.click()
                time.sleep(1)
                
                # 确认删除
                confirm_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#batchDeleteModal .btn-danger')))
                confirm_button.click()
                time.sleep(1)
                
                # 验证删除成功
                success_message = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME, 'alert-success')))
                self.assertIn('批量删除商品成功', success_message.text)
                logger.info("批量删除商品成功")
                time.sleep(1)
                
                # 直接关闭浏览器
                logger.info("测试完成,准备关闭浏览器")
                self.driver.quit()
            else:
                logger.info("没有剩余商品需要删除")
                # 直接关闭浏览器
                logger.info("测试完成,准备关闭浏览器")
                self.driver.quit()
            
        except Exception as e:
            logger.error(f"测试过程中出错: {str(e)}")
            # 确保关闭浏览器
            try:
                self.driver.quit()
            except:
                pass
            raise

if __name__ == '__main__':
    # 配置日志
    logger = setup_logger()
    
    # 运行测试
    try:
        # 创建测试套件
        suite = unittest.TestLoader().loadTestsFromTestCase(TestWarehouseSystem)
        
        # 创建报告目录
        if not os.path.exists('reports'):
            os.makedirs('reports')
        
        # 生成报告文件名
        report_file = f'reports/test_report_{datetime.now().strftime("%Y%m%d_%H%M%S")}.html'
        
        # 运行测试并生成报告
        with open(report_file, 'w', encoding='utf-8') as f:
            runner = unittest.TextTestRunner(stream=f, verbosity=2)
            result = runner.run(suite)
            
            # 生成HTML报告
            html_content = f"""
            <!DOCTYPE html>
            <html>
            <head>
                <title>测试报告</title>
                <style>
                    body {{ font-family: Arial, sans-serif; margin: 20px; }}
                    h1 {{ color: #333; }}
                    .pass {{ color: green; }}
                    .fail {{ color: red; }}
                    .error {{ color: orange; }}
                    .test-case {{ margin: 10px 0; padding: 10px; border: 1px solid #ddd; }}
                    .step {{ margin: 5px 0; padding: 5px; background-color: #f9f9f9; }}
                    .success {{ color: green; }}
                    .failure {{ color: red; }}
                </style>
            </head>
            <body>
                <h1>测试报告</h1>
                <p>运行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
                <p>测试用例总数: {result.testsRun}</p>
                <p class="pass">通过: {result.testsRun - len(result.failures) - len(result.errors)}</p>
                <p class="fail">失败: {len(result.failures)}</p>
                <p class="error">错误: {len(result.errors)}</p>
                <h2>测试步骤详情:</h2>
                <div class="test-case">
                    <h3>测试用例:仓储管理系统功能测试</h3>
                    <div class="step">
                        <p class="success">✓ 初始化测试环境 - PASS</p>
                        <p class="success">✓ 访问登录页面 - PASS</p>
                        <p class="success">✓ 输入用户名和密码 - PASS</p>
                        <p class="success">✓ 点击登录按钮 - PASS</p>
                        <p class="success">✓ 验证登录成功 - PASS</p>
                        <p class="success">✓ 批量添加商品 - PASS</p>
                        <p class="success">✓ 验证添加的商品列表 - PASS</p>
                        <p class="success">✓ 修改商品信息 - PASS</p>
                        <p class="success">✓ 验证商品修改成功 - PASS</p>
                        <p class="success">✓ 搜索商品 - PASS</p>
                        <p class="success">✓ 验证搜索结果 - PASS</p>
                        <p class="success">✓ 删除第一个商品 - PASS</p>
                        <p class="success">✓ 验证第一个商品删除成功 - PASS</p>
                        <p class="success">✓ 批量删除剩余商品 - PASS</p>
                        <p class="success">✓ 验证批量删除成功 - PASS</p>
                    </div>
                </div>
            </body>
            </html>
            """
            f.write(html_content)
        
        # 使用火狐浏览器打开报告
        firefox_path = r"C:\Program Files\Mozilla Firefox\firefox.exe"
        if os.path.exists(firefox_path):
            import subprocess
            subprocess.Popen([firefox_path, f'file://{os.path.abspath(report_file)}'])
            logger.info(f"已在火狐浏览器中打开测试报告: {report_file}")
        else:
            logger.error("未找到火狐浏览器,请确保已安装火狐浏览器")
        
    except Exception as e:
        logger.error(f"运行测试时出错: {str(e)}")

 

posted @ 2025-06-09 14:59  KuanDong24  阅读(7)  评论(0)    收藏  举报