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

基于NUnit的Page Object Model登录场景浏览器实例化问题

解决NUnit+POM登录成功后重新实例化浏览器窗口的问题

我注意到你现在的问题是:[SetUp]方法只在整个测试方法执行前初始化一次Driver,当登录成功注销并关闭窗口后,后续循环里的测试就没有可用的浏览器实例了,导致下一次操作元素时会报错。下面我来给你梳理解决方案和修改后的代码。

问题根源分析

你的Perform_VSS_Login测试方法是循环处理Excel中的多组登录凭证,但NUnit的[SetUp]只会在这个测试方法启动时创建一次ChromeDriver实例。当某次登录成功后,你调用DriverInstance.driver.Close()关闭了窗口,此时Driver实例已经失效,后续循环迭代再去操作Userid、Password这些元素时,就会抛出“会话已结束”之类的错误。

解决方案

我们需要在每次注销关闭窗口后,重新初始化Driver实例并导航到登录页面,同时重新绑定页面对象与新的Driver。另外,还要用[TearDown]方法确保测试结束后彻底清理所有浏览器资源。

第一步:完善Driver管理类

先把Driver的初始化和关闭逻辑封装成可复用的静态方法,方便在循环中调用:

public static class DriverInstance
{
    public static IWebDriver driver;

    // 封装Driver初始化逻辑,先清理旧实例再创建新的
    public static void InitializeDriver()
    {
        if (driver != null)
        {
            driver.Quit();
            driver.Dispose();
        }
        driver = new ChromeDriver();
        driver.Navigate().GoToUrl("你的登录页面URL"); // 替换成实际的登录页地址
        driver.Manage().Window.Maximize();
    }

    // 封装Driver销毁逻辑,确保彻底释放资源
    public static void QuitDriver()
    {
        if (driver != null)
        {
            driver.Quit();
            driver.Dispose();
            driver = null;
        }
    }
}

第二步:修改测试主类

利用[SetUp]初始化第一次Driver,[TearDown]确保测试结束后清理资源:

using NUnit.Framework;

namespace PracticePrograms.PageObjectsWithPageFactory
{
    public class Tests
    {
        [SetUp]
        public void Setup()
        {
            // 初始化第一次浏览器实例
            DriverInstance.InitializeDriver();
        }

        [Test]
        public void Perform_VSS_Login()
        {
            VSSLoginObjects loginPage = new VSSLoginObjects();
            loginPage.Test_Login_QA_Portal();
        }

        [TearDown]
        public void TearDown()
        {
            // 测试结束后彻底销毁Driver
            DriverInstance.QuitDriver();
        }
    }
}

第三步:重构登录页面对象类

调整登录方法,在成功注销后重新初始化Driver并绑定页面对象:

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using SeleniumExtras.PageObjects;
using System.Collections.Generic;

namespace PracticePrograms.PageObjectsWithPageFactory
{
    public class VSSLoginObjects
    {
        [FindsBy(How = How.Id, Using = "userNameOldLook")]
        public IWebElement Userid { get; set; }

        [FindsBy(How = How.XPath, Using = "//input[@type='password']")]
        public IWebElement Password { get; set; }

        [FindsBy(How = How.XPath, Using = "//a[contains(text(),'login')]")]
        public IWebElement Btn_Login { get; set; }

        [FindsBy(How = How.XPath, Using = "//li[contains(text(),'Login failed')]")]
        public IWebElement Lbl_Login_Failed { get; set; }

        [FindsBy(How = How.XPath, Using = "//a[contains(text(),'Logoff')]")]
        public IWebElement lnk_logoff;

        public VSSLoginObjects()
        {
            // 初始化时绑定当前Driver
            PageFactory.InitElements(DriverInstance.driver, this);
        }

        // 重构登录方法,返回登录是否成功的结果
        private bool Login(string username, string password)
        {
            Userid.SendKeys(username);
            Wait.UntilElementDisplayed(DriverInstance.driver, Password);
            Password.SendKeys(password);
            Btn_Login.Click();

            // 通过页面特征判断登录是否成功
            return DriverInstance.driver.PageSource.Contains("Trucks Portal");
        }

        public void Test_Login_QA_Portal()
        {
            string excelPath = @"C:\\Selenium Files\\TestData.xlsx";
            int rowCount = ExcelClassTemp.GetRowCount(excelPath, 3);
            // 收集失败的用例,最后统一断言
            var failedCredentials = new List<string>();

            for (int i = 2; i <= rowCount; i++)
            {
                string username = ExcelClassTemp.ReadExcel(excelPath, 3, i, 1).ToUpper();
                bool loginSuccess = Login(username, "Welcome567");

                if (loginSuccess)
                {
                    Console.WriteLine($"✅ User Credentials {username} Passed");
                    // 执行注销
                    lnk_logoff.Click();
                    // 关闭当前窗口
                    DriverInstance.driver.Close();
                    // 重新初始化Driver和页面对象
                    DriverInstance.InitializeDriver();
                    PageFactory.InitElements(DriverInstance.driver, this);
                }
                else
                {
                    Console.WriteLine($"❌ User Credentials {username} Failed");
                    failedCredentials.Add(username);
                    // 清空输入框,准备下一次测试
                    Userid.Clear();
                    Password.Clear();
                }
            }

            // 最后断言:如果有失败的用例,抛出错误
            Assert.IsEmpty(failedCredentials, $"以下凭证登录失败:{string.Join(", ", failedCredentials)}");
        }
    }
}

额外优化建议

  1. 使用参数化测试:如果Excel中的数据量较大,建议用NUnit的[TestCaseSource]特性,把每个凭证作为单独的测试用例。这样[SetUp]和[TearDown]会为每个用例执行一次,更符合测试框架的最佳实践,也能在测试报告中看到每个用例的独立状态。
  2. 显式等待优化:把Wait.UntilElementDisplayed封装成通用方法,避免重复代码。
  3. 避免硬编码密码:把测试密码放到配置文件中,不要硬编码在代码里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:47:02