Extent Reports V4在Selenium C#中并行测试结果被覆盖的问题求助
解决Extent Reports V4并行测试结果被覆盖的问题
问题根源
- 非线程安全的测试实例:
test作为类级变量,并行测试时多个线程会覆盖彼此的测试实例,最终只有最后执行的线程结果被记录 - 报告文件写入冲突:所有线程同时写入同一个
index.html文件,默认模式下会覆盖之前的内容 - ExtentReports实例重复创建:如果多个测试类继承
DriverHelper,OneTimeSetUp会多次创建ExtentReports实例,导致报告内容被覆盖
修复方案及代码修改
关键修改点:
- 用
ThreadLocal<ExtentTest>存储每个线程的测试实例,避免跨线程覆盖 - 为
ExtentHtmlReporter启用追加模式,允许多线程写入同一报告文件 - 将
ExtentReports改为单例模式,确保整个测试过程只有一个实例
修改后的完整代码:
public class DriverHelper { // 单例ExtentReports实例,确保全局唯一 private static readonly Lazy<ExtentReports> _extentInstance = new Lazy<ExtentReports>(() => { string workingDirectory = Environment.CurrentDirectory; string projectDirectory = Directory.GetParent(workingDirectory).Parent.Parent.FullName; string reportPath = Path.Combine(projectDirectory, "index.html"); var htmlReporter = new ExtentHtmlReporter(reportPath); // 启用追加模式,解决并行写入覆盖问题 htmlReporter.AppendExisting = true; var extent = new ExtentReports(); extent.AttachReporter(htmlReporter); extent.AddSystemInfo("Host Name", "Gold end to end"); extent.AddSystemInfo("Tester", "Arshad"); return extent; }); public static ExtentReports Extent => _extentInstance.Value; // ThreadLocal存储每个线程的ExtentTest实例 public ThreadLocal<ExtentTest> Test = new ThreadLocal<ExtentTest>(); public ThreadLocal<IWebDriver> Driver = new ThreadLocal<IWebDriver>(); [OneTimeSetUp] public void Setup() { // 单例已在Lazy中初始化,此处无需重复创建 } [SetUp] public void StartBrowser() { // 为当前线程创建独立的测试实例 Test.Value = Extent.CreateTest(TestContext.CurrentContext.Test.Name); var browserSetup = new BrowserSetup(); Driver.Value = browserSetup.SetupBrowser(); } [TearDown] public void TearDown() { var status = TestContext.CurrentContext.Result.Outcome.Status; var stackTrace = TestContext.CurrentContext.Result.StackTrace; DateTime time = DateTime.Now; string fileName = $"Screenshot_{time.ToString("h_mm_ss")}.png"; if (status == TestStatus.Failed) { Test.Value.Fail("Test failed", CaptureScreenshot(Driver.Value, fileName)); Test.Value.Log(Status.Fail, $"Test failed with logtrace: {stackTrace}"); } else if (status == TestStatus.Passed) { Test.Value.Log(Status.Pass, "Test successful"); } Driver.Value.Quit(); // 移除当前线程的Test实例,避免内存泄漏 Test.Dispose(); } [OneTimeTearDown] public void OneTimeTearDown() { Extent.Flush(); } public MediaEntityModelProvider CaptureScreenshot(IWebDriver driver, string screenShotName) { ITakesScreenshot ts = (ITakesScreenshot)driver; var screenshot = ts.GetScreenshot().AsBase64EncodedString; return MediaEntityBuilder.CreateScreenCaptureFromBase64String(screenshot, screenShotName).Build(); } }
额外注意事项
- 确保NUnit的并行配置正确(在
nunit.config中启用并行执行) - 截图文件名建议加入线程ID,避免多个线程生成同名截图文件:
string fileName = $"Screenshot_{Thread.CurrentThread.ManagedThreadId}_{time.ToString("h_mm_ss")}.png"; - 单例模式使用
Lazy<T>确保线程安全的初始化
内容的提问来源于stack exchange,提问作者paul satham
相关产品推荐
相关产品推荐

