如何在JUnit5 Playwright测试中仅失败时保留.har文件?
JUnit5 + Playwright 仅在测试失败时保留HAR文件
问题背景
我在用JUnit5写Playwright测试,需求是仅当测试因断言失败、超时等情况失败时保存.har文件,测试通过则自动丢弃。现在的实现是在@BeforeEach里启动HAR录制,但不管测试结果如何都会保留文件。试过在断言后用File.delete()删除,但这种方法不够优雅,也没找到停止HAR生成的相关文档。
当前代码:
@Test public void myTest() { try { // .har文件已在录制 navigateToSomePages(); assertThat(page.locator(cssSelector)).hasText("allGood"); } catch (AssertionFailedError | TimeoutError error) { // 不管测试结果如何,.har文件都会保留,只想在失败/断言抛出时保留 doScreenShot(); throw error; } } @BeforeEach // 每个测试前启动HAR录制 protected void beforeEachAbstractMethod() { context = browser.newContext(new Browser.NewContextOptions() .setRecordHarPath(Paths.get("file.har")) .setRecordHarMode(HarMode.MINIMAL)); }
Playwright HAR录制核心规则(翻译自官方文档)
- HAR录制与浏览器上下文绑定,只有当上下文关闭时,才会将录制的内容写入指定路径的文件;
- 可通过
setRecordHarPath指定HAR文件路径,setRecordHarMode控制录制内容的详细程度(MINIMAL模式仅记录请求URL、状态码和响应头等关键信息); - 若未指定
recordHarPath,HAR内容会暂存于内存,可通过context.har()获取,但不会写入本地文件。
优化方案
方案一:利用JUnit5 TestWatcher扩展(推荐)
通过JUnit5的测试生命周期监听接口,统一控制所有测试用例的HAR文件保留逻辑,无需在每个测试中重复编写代码。
完整实现代码
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.TestWatcher; import org.junit.jupiter.api.extension.ExtensionContext; import org.assertj.core.api.Assertions; import com.microsoft.playwright.Browser; import com.microsoft.playwright.BrowserContext; import com.microsoft.playwright.HarMode; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Optional; @ExtendWith(HARRetentionExtension.class) public class PlaywrightHARTest { private Browser browser; private BrowserContext context; private Path tempHarPath; // 失败测试HAR文件保存目录 private static final Path FAILED_HAR_DIR = Paths.get("failed-tests-har"); static { // 初始化保存目录,不存在则创建 FAILED_HAR_DIR.toFile().mkdirs(); } @BeforeEach void setupTest() { // 生成唯一临时HAR路径,避免并行测试文件冲突 tempHarPath = Paths.get(System.getProperty("java.io.tmpdir"), "test-har-" + System.currentTimeMillis() + ".har"); // 启动HAR录制到临时文件 context = browser.newContext(new Browser.NewContextOptions() .setRecordHarPath(tempHarPath) .setRecordHarMode(HarMode.MINIMAL)); } @AfterEach void teardownTest() { // 关闭上下文,触发HAR内容写入临时文件 if (context != null) { context.close(); } } @Test void testPageContent() { navigateToTargetPage(); Assertions.assertThat(page.locator("#content")).hasText("Expected Content"); } // 自定义JUnit5扩展,监听测试结果处理HAR文件 public static class HARRetentionExtension implements TestWatcher { @Override public void testFailed(ExtensionContext context, Throwable cause) { PlaywrightHARTest testInstance = context.getTestInstance(PlaywrightHARTest.class).get(); // 测试失败:将临时HAR文件移动到保存目录,用测试类+方法名命名 Path targetPath = FAILED_HAR_DIR.resolve( context.getTestClass().get().getSimpleName() + "-" + context.getTestMethod().get().getName() + ".har" ); try { Files.move(testInstance.tempHarPath, targetPath); } catch (IOException e) { e.printStackTrace(); } } @Override public void testSuccessful(ExtensionContext context) { PlaywrightHARTest testInstance = context.getTestInstance(PlaywrightHARTest.class).get(); // 测试成功:删除临时HAR文件 try { Files.deleteIfExists(testInstance.tempHarPath); } catch (IOException e) { e.printStackTrace(); } } } // 页面导航辅助方法 private void navigateToTargetPage() { // 实现页面跳转逻辑 } }
核心优势
- 统一管理:所有测试的HAR文件逻辑集中在扩展中,无需重复编写;
- 避免冲突:用时间戳生成临时文件,解决并行测试时的文件覆盖问题;
- 优雅可控:通过官方生命周期接口监听测试状态,逻辑清晰。
方案二:测试内finally块处理(轻量版)
如果不想编写扩展,可在单个测试方法中通过finally块处理,适合少量测试的场景:
@Test public void myTest() { boolean testSuccess = true; try { navigateToSomePages(); assertThat(page.locator(cssSelector)).hasText("allGood"); } catch (AssertionFailedError | TimeoutError error) { doScreenShot(); testSuccess = false; throw error; } finally { // 关闭上下文,确保HAR文件写入 context.close(); if (testSuccess) { // 测试通过,删除HAR文件 try { Files.deleteIfExists(Paths.get("file.har")); } catch (IOException e) { e.printStackTrace(); } } } }
内容的提问来源于stack exchange,提问作者andrei
相关产品推荐
相关产品推荐

