基于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)}"); } } }
额外优化建议
- 使用参数化测试:如果Excel中的数据量较大,建议用NUnit的
[TestCaseSource]特性,把每个凭证作为单独的测试用例。这样[SetUp]和[TearDown]会为每个用例执行一次,更符合测试框架的最佳实践,也能在测试报告中看到每个用例的独立状态。 - 显式等待优化:把
Wait.UntilElementDisplayed封装成通用方法,避免重复代码。 - 避免硬编码密码:把测试密码放到配置文件中,不要硬编码在代码里。
内容的提问来源于stack exchange,提问作者Manish B
相关产品推荐
相关产品推荐

