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

使用Playwright C#的OneTimeSetUp时Page出现空引用异常

问题原因

PageTest基类的Page属性是在每个测试的[SetUp]阶段才完成初始化的,而[OneTimeSetUp]是在测试Fixture初始化时执行,这个时机早于Page的创建流程,所以直接访问Page会抛出空引用异常。

解决方案

要在[OneTimeSetUp]中完成登录并存储认证状态,需要手动管理Playwright的Browser、Context和Page实例,而非依赖PageTest提供的属性。具体实现如下:

  1. 在Fixture类中声明Browser、IBrowserContext和IPage私有字段
  2. 在[OneTimeSetUp]中初始化Playwright、Browser、Context和Page,完成登录并保存认证状态
  3. 在[OneTimeTearDown]中清理资源,按需执行登出并销毁Context和Browser
  4. 测试方法可直接使用PageTest提供的Page属性,它会自动加载已保存的认证状态

修改后的代码示例:

using Microsoft.Playwright;
using Microsoft.Playwright.NUnit;
using System;

namespace PlaywrightTests
{
    [Parallelizable(ParallelScope.Fixtures)]
    public class Tests1V2 : PageTest
    {
        private string site = "https://google.co.uk/";
        private IBrowser? _browser;
        private IBrowserContext? _context;
        private IPage? _oneTimePage;

        [OneTimeSetUp]
        public async Task OneTimeSetUp()
        {
            // 初始化Playwright并启动浏览器
            var playwright = await Playwright.CreateAsync();
            _browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
            {
                Headless = true // 根据测试需求调整是否启用无头模式
            });

            // 创建上下文并初始化页面
            _context = await _browser.NewContextAsync();
            _oneTimePage = await _context.NewPageAsync();

            // 执行登录流程(替换为你的实际登录逻辑)
            await _oneTimePage.GotoAsync(site);
            // 示例:输入账号密码、点击登录按钮
            // await _oneTimePage.FillAsync("#username", "your-account");
            // await _oneTimePage.FillAsync("#password", "your-password");
            // await _oneTimePage.ClickAsync("#submit-login");

            // 保存认证状态到本地文件
            await _context.StorageStateAsync(new BrowserContextStorageStateOptions
            {
                Path = "state.json"
            });
        }

        [OneTimeTearDown]
        public async Task OneTimeTearDown()
        {
            // 清理资源,按需执行登出操作
            if (_oneTimePage != null)
            {
                // await _oneTimePage.GotoAsync("/logout");
                await _oneTimePage.CloseAsync();
            }
            if (_context != null) await _context.CloseAsync();
            if (_browser != null) await _browser.CloseAsync();
        }

        public override BrowserNewContextOptions ContextOptions()
        {
            var contextOptions = new BrowserNewContextOptions();
            // 让后续测试的上下文自动加载已保存的认证状态
            contextOptions.StorageStatePath = "state.json";
            
            if (Environment.GetEnvironmentVariable("width") != null 
                && int.TryParse(Environment.GetEnvironmentVariable("width"), out int width))
            {
                contextOptions.ViewportSize = new ViewportSize { Width = width, Height = 1080 };
            }
            return contextOptions;
        }

        [Test]
        public async Task AALoginTest1()
        {
            // 直接使用PageTest提供的Page属性,已自动携带登录态
            await Page.GotoAsync(site);
            // var mainPage = new MainPage(Page);
            // var checkContact = await mainPage.checkLoggedIn();
            // Assert.IsTrue(checkContact);
        }
    }
}
关键说明
  • 手动创建的_oneTimePage仅用于执行一次性登录操作,完成后保存认证状态
  • 后续测试的Page属性会通过ContextOptions()中指定的StorageStatePath自动加载登录态,无需重复执行登录流程
  • [OneTimeTearDown]中统一清理资源,避免内存泄漏,同时可按需执行登出操作重置状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 08:18:32