You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python+Selenium自动化测试:HTML报告无日志的解决求助

问题

我是Stack Overflow、自动化测试及编程领域的新手,还请多多包涵。我已为公司完成359个自动化测试用例,且全部执行通过。在学习Selenium课程时,我了解到日志功能及通过pytest -s -v --html=report.html命令生成HTML测试报告的方法。

目前遇到的问题是:生成的report.html仅显示每个测试用例的名称,无任何日志内容。我希望能在代码中添加日志并将其显示在HTML报告中,方便非技术人员了解测试执行过程及定位失败原因。

以下是我的测试脚本片段(重复代码已封装到类中的fixture,运行正常):

import pytest
import unittest
import time
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException, TimeoutException
import os
from dotenv import load_dotenv
load_dotenv()
import random
import log_config
logger = log_config.set_up_logging()
chromedriver_path = os.environ.get("CHROME_PATH")
os.environ["PATH"] += os.pathsep + chromedriver_path
from settings import CON_SELFSERVICE_URL
from locators import *
from baseclass import BaseClass


class Test_CON_02_Coupons(unittest.TestCase, BaseClass):
    @pytest.fixture(autouse=True)
    def setup(self, request):
        chrome_options = Options()
        chrome_options.add_argument("--window-size=1920,1080")
        chrome_options.add_argument("--start-maximized")
        chrome_options.add_argument("--headless")
        chrome_options.add_argument("--no-sandbox")
        self.driver = webdriver.Chrome(options=chrome_options)
        self.driver.maximize_window()
        self.driver.implicitly_wait(60)
        self.vars = {}
        self.driver.get(CON_SELFSERVICE_URL)
        yield
        self.driver.quit()
    
    @pytest.fixture(autouse=True)
    def login_and_navigate(self, setup):
        self.driver.find_element(*username_field).click()
        self.driver.find_element(*username_field).send_keys(valid_username)
        self.driver.find_element(*password_field).click()
        self.driver.find_element(*password_field).send_keys(valid_password)
        WebDriverWait(self.driver, 30).until(EC.element_to_be_clickable(login_button))
        time.sleep(0.5)
        self.driver.find_element(*login_button).click()
        time.sleep(1) 
        alert_visible = self.driver.execute_script("""
            var element = document.querySelector(arguments[0]);
            return element !== null && element.offsetParent !== null;
        """, Login_Alert[1])
        if alert_visible:
            self.driver.find_element(*username_field).click()
            self.driver.find_element(*username_field).clear()
            self.driver.find_element(*username_field).send_keys(valid_username)
            self.driver.find_element(*password_field).click()
            self.driver.find_element(*password_field).clear()
            self.driver.find_element(*password_field).send_keys(valid_password)
            WebDriverWait(self.driver, 30).until(EC.element_to_be_clickable(login_button))
            time.sleep(0.5)
            self.driver.find_element(*login_button).click()
        self.wait = WebDriverWait(self.driver, 30)
        WebDriverWait(self.driver, 30).until(EC.presence_of_element_located((menu_couponsv3)))
        self.driver.find_element(*menu_couponsv3).click()
        iframe = self.wait.until(EC.presence_of_element_located((coupons_iframe)))
        self.driver.switch_to.frame(iframe)

    def test_coupons_03_Search_by_Title(self):
        # Being able to conduct a coupon search based on a Title
        log = self.getLogger() 
        self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".app-headline__subtitle")))
        self.driver.find_element(By.XPATH, "//input[contains(@id,'mat-input-0')]").click()
        log.info("Click on search bar")
        self.driver.find_element(By.ID, "mat-input-0").send_keys("100")
        log.info("Input of -100-")
        assert "100" in self.driver.find_element(By.CSS_SELECTOR, 'td.mat-cell.cdk-cell.--long-text.--is-clickable.cdk-column-i18nFields.mat-column-i18nFields.ng-star-inserted > span').text
        log.info("Checking that the inputed value is in the title of the first option of the grid")
        self.driver.find_element(By.ID, "mat-input-0").click()
        log.info("Click on search bar")
        self.driver.find_element(By.ID, "mat-input-0").clear()

我通过baseclass.py中的getLogger方法将日志输出到logfile.log文件,内容正常,希望能将同样的日志展示在HTML报告中。baseclass.py内容如下:

import inspect
import logging

class BaseClass:
    def getLogger(self):
        loggerName= inspect.stack()[1][3]
        logger = logging.getLogger(loggerName)
        fileHandler = logging.FileHandler("logfile.log")
        formatter = logging.Formatter("%(asctime)s :%(levelname)s : %(name)s :%(message)s")
        fileHandler.setFormatter(formatter)

        logger.addHandler(fileHandler)
        logger.setLevel(logging.DEBUG)
        return logger
解决方法

要把日志集成到pytest-html生成的报告中,核心是让日志同时输出到控制台(pytest -s会捕获控制台输出并写入报告),具体修改如下:

1. 修改BaseClass中的日志配置

在getLogger方法中新增控制台处理器,让日志同时输出到文件和控制台,同时避免重复添加处理器:

import inspect
import logging

class BaseClass:
    def getLogger(self):
        loggerName= inspect.stack()[1][3]
        logger = logging.getLogger(loggerName)
        
        # 避免重复添加处理器,防止日志重复输出
        if logger.hasHandlers():
            return logger
            
        # 文件处理器(保留原有输出到logfile.log的功能)
        fileHandler = logging.FileHandler("logfile.log")
        file_formatter = logging.Formatter("%(asctime)s :%(levelname)s : %(name)s :%(message)s")
        fileHandler.setFormatter(file_formatter)
        
        # 控制台处理器(新增,用于输出到控制台供pytest-html捕获)
        streamHandler = logging.StreamHandler()
        # 可选:给控制台日志设置更简洁的格式,避免报告中信息冗余
        stream_formatter = logging.Formatter("%(levelname)s : %(message)s")
        streamHandler.setFormatter(stream_formatter)
        
        logger.addHandler(fileHandler)
        logger.addHandler(streamHandler)
        logger.setLevel(logging.DEBUG)
        return logger

2. 确保测试命令参数正确

运行测试时必须保留-s参数(禁用pytest的输出捕获),这样控制台日志才能被pytest-html捕获并写入报告:

pytest -s -v --html=report.html

修改完成后,重新运行测试,生成的HTML报告中就会显示每个测试用例执行过程中的日志信息,和logfile.log中的内容对应,方便非技术人员查看测试流程和定位问题。


内容的提问来源于stack exchange,提问作者Martin M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 17:42:44