ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Selenium .NET 测试指南:掌握基于 NUnit、DriverTestFixture 与 Bazel 的跨浏览器 WebDriver 测试编写

Selenium .NET 测试指南:掌握基于 NUnit、DriverTestFixture 与 Bazel 的跨浏览器 WebDriver 测试编写 Selenium .NET 测试指南掌握基于 NUnit、DriverTestFixture 与 Bazel 的跨浏览器 WebDriver 测试编写【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/seleniumSelenium 的 .NET 语言绑定在仓库的dotnet/目录下维护并随整个项目一起用 Bazel 构建与验证。本文以 dotnet/TESTING.md 为核心骨架完整讲解在该代码库中编写与运行 NUnit 测试的约定如何继承DriverTestFixture、如何用[IgnoreBrowser]/[NeedsFreshDriver]等基础设施特性控制浏览器与驱动生命周期、如何用 Bazel 在单类、单浏览器粒度上运行测试以及如何在 Rider / Visual Studio 中做日常调试。读者读完本文即可上手为 Selenium .NET 编写规范、可跨浏览器复用的测试用例并能理解底层属性实现与 Bazel 套件展开机制。测试框架选型与基础设施约定Selenium .NET 测试体系有四项核心约定与 dotnet/TESTING.md 一致测试框架为 NUnit所有断言使用 NUnit 的Assert.That(...)语法所有测试类都继承自DriverTestFixture位于 dotnet/test/webdriver/DriverTestFixture.cs由它统一管理 driver 实例、测试页 URL 与等待能力测试用 HTML 页面通过 URL 属性访问如simpleTestPage、javascriptPage这类概念性属性。在当前源码实现中DriverTestFixture通过EnvironmentManager.Instance.WebServer.Urls暴露一个静态UrlBuilder见 dotnet/test/webdriver/DriverTestFixture.cs测试里实际写成Driver.Url Urls.XhtmlTestPage或Urls.NestedPage参考 dotnet/test/webdriver/ElementFindingTests.cs测试页面本体存放在common/src/web/并作为 Bazeldata依赖打包进测试运行环境见 dotnet/test/webdriver/BUILD.bazel 中的//common/src/webWaitForT()提供默认 5 秒超时的轮询等待用于把异步渲染或条件变化等到预期状态再继续断言。驱动与服务器的运行环境由谁拉起DriverTestFixture本身并不创建浏览器真正的环境装配发生在单例EnvironmentManager的构造函数中见 dotnet/test/webdriver/Infrastructure/Environment/EnvironmentManager.cs。它一次性完成读取appconfig.json解析当前驱动配置 → 用DriverFactory实例化驱动 → 把驱动类型全程序集搜索解析为DriverConfig.DriverTypeName→ 启动本地AppServer托管测试页面 → 定位 selenium-manager 二进制并设置SE_MANAGER_PATH→ 必要时启动远程 Selenium Server。而DriverTestFixture则在[OneTimeSetUp]中调用EnvironmentManager.Instance.GetCurrentDriver()并在[OneTimeTearDown]中关闭 driverdotnet/test/webdriver/DriverTestFixture.cs。DriverTestFixture的[TearDown]还会做错误自愈一旦测试结果为 Error就Dispose当前 driver 并新建一个干净的实例避免失败状态污染后续用例同文件 L60-L68。第一个可运行的测试类依据文档的指导一个典型的测试类如下[TestFixture] public class MyFeatureTest : DriverTestFixture { [Test] public void ShouldFindElement() { driver.Url simpleTestPage; IWebElement element driver.FindElement(By.Id(foo)); Assert.That(element.Text, Is.EqualTo(expected)); } [Test] [IgnoreBrowser(Browser.Safari, Safari doesnt support this)] public void ShouldDoSomething() { // Skipped on Safari } }对照仓库中真实的测试类如 dotnet/test/webdriver/ElementFindingTests.cs可以归纳出与实际一致的写法要点类声明[TestFixture]并且继承DriverTestFixture从而拿到Driver属性用[Test]标注每个用例用例方法名保持Should...的描述性风格页面 URL 直接使用基类的Urls构建器如Urls.XhtmlTestPage所有.cs测试文件放在dotnet/test/webdriver/下命名以Tests.cs结尾即能被套件识别见下文构建文件一节例如ElementFindingTests.cs、ClickTests.cs、CookieTests.cs、BiDi/BrowsingContext/BrowsingContextTests.cs等声明命名空间OpenQA.Selenium.Tests与基类保持一致。用 Bazel 运行 .NET 测试测试目标的生成机制文档明确指出整套测试只编译一次成一个二进制Bazel 再为每个测试类生成一个目标并为每种支持的浏览器生成一个变体。这一机制可以在 dotnet/private/dotnet_nunit_test_suite.bzl 中得到完整印证dotnet_nunit_test_suite先调用csharp_test把所有*.cs编译成单个测试程序集并将该csharp_test目标标记为manualL198-L207随后遍历每个以Test.cs/Tests.cs结尾的文件生成一个包装测试_test_wrapper_test通过--filter FullyQualifiedName~.ClassName只执行对应类L209-L235若某目标browsers列表含多种浏览器则类名后缀拼上浏览器名例如ElementFindingTests-chrome只给默认浏览器列表第一个额外生成不带后缀的裸类名测试目标native.test_suiteL222-L226不同浏览器目标通过--test-parameter ActiveDriverConfigChrome/Firefox/...把配置传给 NUnit 参数从而决定EnvironmentManager选用哪个 driver 配置_BROWSERS 映射见该文件 L10-L105。最常用的运行命令测试代码位于//dotnet/test/webdriver运行命令如下注意每个类都会按浏览器矩阵展开因此单类目标既可跑默认浏览器也可显式指定浏览器bazel test //dotnet/test/webdriver/... --pin_browserstrue # 全部测试 × 全部支持的浏览器 bazel test //dotnet/test/webdriver:ElementFindingTests --pin_browserstrue # 单个类默认浏览器 bazel test //dotnet/test/webdriver:ElementFindingTests-chrome --pin_browserstrue # 单个类 × Chrome bazel test //dotnet/test/webdriver:ElementFindingTests-edge --pin_browserstrue # 单个类 × Edge额外参数失败重试与完整日志bazel test //dotnet/test/webdriver/... --flaky_test_attempts3 --pin_browserstrue # 失败自动重试 3 次 bazel test //dotnet/test/webdriver/... --test_outputall --pin_browserstrue # 输出全部测试日志--flaky_test_attempts对偶发性失败如 WebDriver 会话竞争很实用--test_outputall则在用例失败排查时需要查看完整 stdout 时使用。另外在 dotnet/private/dotnet_nunit_test_suite.bzl 中测试运行器传入了--ignore-exit-code 8这是因为退出码 8 表示零测试执行——当某类全部被[IgnoreBrowser]、[Ignore]过滤掉时不应导致构建失败真正的测试失败仍以退出码 2 呈现。为什么总是加--pin_browsers--pin_browsers控制测试使用仓库固定的浏览器与驱动版本而非机器上随意安装的版本。当前 dotnet/test/webdriver/BUILD.bazel 中声明的浏览器列表是firefox # 列表第一个 → 默认浏览器 safari ie edge chrome因此裸类目标如:ElementFindingTests默认跑Firefox。而_BROWSERS映射进一步限定了平台safari目标带target_compatible_with osx、ie目标仅兼容windows见 dotnet/private/dotnet_nunit_test_suite.bzl跨平台自动跳过不兼容目标。当前 BUILD.bazel 的target_frameworks [net10.0]表明测试面向 .NET 10 编译后文[IgnoreTarget(net48, ...)]仅作为历史遗留的框架名示例实际生效的 TFM 名见IgnoreTargetAttribute一节。让--pin_browsers成为默认每次敲--pin_browserstrue很繁琐可以在仓库根目录的.bazelrc.local个人本地配置不入库中一次性设置build --//common:pin_browsers在 IDE 中运行测试Rider / Visual StudioBazel 是 CI 与发版验证的唯一事实来源但日常开发内环可以打开 IDE 直接跑 NUnit 测试用 Rider 或 Visual Studio 打开解决方案文件 dotnet/Selenium.slnx在测试资源管理器中通过内置 NUnit runner 执行单个用例或整个类IDE 方式与 Bazel 结果基本一致除非bazel build与dotnet build之间存在构建差异例如不同的目标框架、运行参数或依赖解析因此 push 前务必再用bazel test验证一次改动。值得一提的是如果测试类使用了[NeedsFreshDriver]等基础设施特性IDE 运行也能正常工作因为这些特性依赖的是EnvironmentManager单例而非 Bazel 注入的参数只有浏览器选型ActiveDriverConfig这类参数在 IDE 下需要借助appconfig.json的默认值默认Chrome来解析见 dotnet/test/webdriver/appconfig.json。跳过测试四类 Ignore 特性跳过的统一手段是 NUnit 属性。可用浏览器枚举值来自 dotnet/test/webdriver/Infrastructure/Browser.csBrowser.Chrome、Browser.Firefox、Browser.Edge、Browser.Safari、Browser.IE、Browser.Remote、Browser.All。属性使用场景[IgnoreBrowser(Browser.X, reason)]跳过特定浏览器下的测试[IgnorePlatform(windows, reason)]跳过特定操作系统上的测试[IgnoreTarget(net48, reason)]跳过特定 .NET 目标框架下的测试[Ignore(reason)]完全跳过该测试NUnit 内置示例[Test] [IgnoreBrowser(Browser.Safari, Safari doesnt support multiple instances)] [IgnoreBrowser(Browser.IE, IE is flaky)] public void TestWithMultipleDrivers() { } [Test] [IgnorePlatform(windows, Thread time not supported)] public void TestLinuxOnly() { }源码级解读特性如何工作IgnoreBrowserAttributedotnet/test/webdriver/Infrastructure/IgnoreBrowserAttribute.cs实现 NUnit 的IApplyToTest在用例收集阶段把测试置为Ignored。命中条件有三种browserToIgnore EnvironmentManager.Instance.Browser当前浏览器一致、browserToIgnore Browser.All全局忽略或当前会话是某种浏览器的 Remote/Grid 实例通过RemoteCapabilities字符串chrome/firefox/internet explorer/MicrosoftEdge比对。由于该特性允许AllowMultiple true且可同时标注在方法或类上跨浏览器矩阵如 Safari IE可以直接叠加多个属性。忽略理由中会自动带上当前浏览器名与开发者在属性中填写的 reason测试报告里一目了然。IgnorePlatformAttributedotnet/test/webdriver/Infrastructure/IgnorePlatformAttribute.cs通过RuntimeInformation.IsOSPlatform判断当前操作系统合法值为windows、linux、mac大小写不敏感。注意仓库里真实的 mac 平台标识是mac而非osx。IgnoreTargetAttributedotnet/test/webdriver/Infrastructure/IgnoreTargetAttribute.cs借助编译期#if NET10_0宏返回当前 TFM 名net10意味着该特性只能在当前项目的目标框架下编译文档表格中net48仅是历史示例值修改属性中的字符串不会让特性在旧框架下自动生效。Driver 生命周期管理NeedsFreshDriver默认情况下同一个测试类共享一个 driver 实例。当用例需要新建/重建浏览器会话例如验证多实例重新加载驱动等行为时用[NeedsFreshDriver]在测试前后请求全新 driver属性使用场景[NeedsFreshDriver(IsCreatedBeforeTest true)]测试开始前生成新 driver[NeedsFreshDriver(IsCreatedAfterTest true)]测试结束后生成新 driver[Test] [NeedsFreshDriver(IsCreatedBeforeTest true, IsCreatedAfterTest true)] [IgnoreBrowser(Browser.Safari, Safari doesnt support multiple instances)] public void TestRequiringFreshDriver() { IWebDriver driver2 EnvironmentManager.Instance.CreateDriverInstance(); try { // Test with multiple drivers } finally { driver2.Quit(); } }实现原理NeedsFreshDriverAttribute继承自 NUnit 的TestActionAttributedotnet/test/webdriver/Infrastructure/NeedsFreshDriverAttribute.cs在BeforeTest/AfterTest回调里检查宿主 fixture 是否为DriverTestFixture若是则调用EnvironmentManager.Instance.CreateFreshDriver()并把新 driver 写回fixtureInstance.Driver从而让后续断言拿到全新会话。多 driver 的配套 API用例内部若想再开第二个浏览器直接调EnvironmentManager.Instance.CreateDriverInstance()带DriverOptions重载则用自定义选项创建并记得用try/finally里的Quit()手动关闭正如上面的示例。Helpers 清单基类与单例公开 APIDriverTestFixture提供的成员实际源码见 dotnet/test/webdriver/DriverTestFixture.cs成员说明Driver当前 WebDriver 实例protected internal IWebDriverUrls页面 URL 构建器UrlBuilder文档所描述的simpleTestPage、javascriptPage等测试页即通过它获取WaitForT(waitFunction, timeoutMessage)等待条件成立默认 5 秒超时、100ms 轮询、忽略等待期间抛出的所有异常内部封装WebDriverWait见 L83-L102CreateFreshDriver()关闭旧会话并新建 driver 实例IsNativeEventsEnabled只读属性判断当前 driver 是否开启原生事件能力EnvironmentManager.Instance提供的成员源码见 dotnet/test/webdriver/Infrastructure/Environment/EnvironmentManager.cs成员说明CreateDriverInstance()用当前 driver 类型额外创建一个 driverCreateDriverInstance(options)传入自定义DriverOptions创建 driverCreateFreshDriver()关闭当前会话后重建 driverGetCurrentDriver()返回当前 driver不存在则自动创建CloseCurrentDriver()Quit 当前 driver 并置空引用Browser当前浏览器枚举值决定IgnoreBrowser命中与否RemoteCapabilities远程会话的 capabilities 字符串如chrome、MicrosoftEdgeWebServer/RemoteServer测试页服务器与远程 Grid 服务器句柄测试目录组织与何时不需要改 Bazel文档给出了dotnet/test/的组织结构与仓库实际布局一致dotnet/test/ ├── webdriver/ # WebDriver 功能测试 │ ├── DriverTestFixture.cs # 基类 │ ├── *Tests.cs # 测试文件ElementFindingTests、ClickTests 等 │ ├── BiDi/ · DevTools/ · Firefox/ · IE/ · Interactions/ · Internal/ · VirtualAuthn/ │ └── Infrastructure/ # 自定义特性与环境 │ ├── IgnoreBrowserAttribute.cs │ ├── NeedsFreshDriverAttribute.cs │ └── Environment/ # EnvironmentManager、DriverFactory、RemoteSeleniumServer ├── remote/ # Remote/Grid 相关测试BUILD.bazel 中 browsers [remote]sizelarge 且 flakyTrue └── support/ # 支持库Selenium.Support测试各子套件均有自己的BUILD.bazel//dotnet/test/webdriver见 dotnet/test/webdriver/BUILD.bazel、//dotnet/test/remote见 dotnet/test/remote/BUILD.bazel。remote 套件依赖 webdriver 套件的:test-data并复用其DriverConfigs下的StableChannelRemoteChromeDriver见 dotnet/test/webdriver/Infrastructure/DriverConfigs/StableChannelRemoteChromeDriver.cs配合AutoStartRemoteServertrue驱动本地 Grid。关于构建文件的结论文档原意 宏实现双重确认新增测试通常无需改 Bazelsrcs glob([**/*.cs])会把目录下所有*.cs递归纳入编译dotnet/test/webdriver/BUILD.bazel再经dotnet_nunit_test_suite按文件名结尾Test.cs/Tests.cs自动拆出每个类的独立测试目标dotnet/private/dotnet_nunit_test_suite.bzl因此新建文件只需满足位于dotnet/test/webdriver或相应子套件目录下、文件名以*Tests.cs结尾、继承DriverTestFixture测试运行所需的页面/扩展/驱动数据统一由:test-datafilegroup 提供其中已包含//common/extensions、//common/src/web、三种平台的 selenium-manager 以及//javascript/atoms见 dotnet/test/webdriver/BUILD.bazel新增用例通常也无需追加 data 依赖。环境配置文件浏览器矩阵从哪里来EnvironmentManager在 Bazel 与 IDE 两种运行模式下都会读取 dotnet/test/webdriver/appconfig.json。它内建了 10 个DriverConfig对应真实源码中的驱动配置类dotnet/test/webdriver/Infrastructure/DriverConfigs/ 目录Chrome/ChromeDev→ 稳定版 / Dev 通道 ChromeBrowserValue: ChromeFirefox/FirefoxNightly→ 稳定版 / Nightly 通道 FirefoxEdge/EdgeDev→ 稳定版 / Dev 通道 EdgeEdgeIEMode→ Edge IE 模式BrowserValue: IESafari/SafariTechPreview→ Safari / Technology PreviewRemote→ 远程 ChromeAutoStartRemoteServer: true配置解析优先级是环境变量ACTIVE_DRIVER_CONFIG、DRIVER_SERVICE_LOCATION、BROWSER_LOCATION NUnit--test-parameter参数 appconfig.json默认值见 EnvironmentManager.cs。这也是 Bazel 每浏览器目标通过--test-parameter ActiveDriverConfigChrome注入目标浏览器的原理所在而--pin_browsers打开后DriverServiceLocation与BrowserLocation会被替换为仓库固定的浏览器/驱动绝对路径经由TEST_SRCDIR解析见同文件 L60-L65、L219-L234从而保证 CI 与本地结果可复现。深入阅读路径官方指南原文dotnet/TESTING.md基类与等待实现dotnet/test/webdriver/DriverTestFixture.cs环境装配单例dotnet/test/webdriver/Infrastructure/Environment/EnvironmentManager.cs 与 DriverFactory.cs基础设施特性Browser.cs、IgnoreBrowserAttribute.cs、IgnorePlatformAttribute.cs、IgnoreTargetAttribute.cs、NeedsFreshDriverAttribute.csBazel 套件宏dotnet/private/dotnet_nunit_test_suite.bzl测试目标声明dotnet/test/webdriver/BUILD.bazel 与 dotnet/test/remote/BUILD.bazel浏览器驱动配置 JSONdotnet/test/webdriver/appconfig.json掌握以上约定后新增一个功能测试类只需三步在dotnet/test/webdriver/下创建XxxTests.cs、继承DriverTestFixture、用bazel test //dotnet/test/webdriver:XxxTests --pin_browserstrue验证——剩下的浏览器矩阵展开、页面服务器启动与 driver 生命周期都由这套基础设施替你完成。【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/selenium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表