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

C# Selenium如何根据Appsettings选择执行对应step defs或类

C# Selenium 技术栈下跨平台UI自动化复用实现方案

核心思路是通过抽象页面对象接口隔离平台差异,结合依赖注入在运行时根据配置加载对应平台的页面对象实现,完全复用同一套feature文件和step定义,不需要为两个平台维护重复的测试逻辑。

第一步:定义页面对象抽象层

先把两个平台共有的页面业务行为抽成公共接口,只定义业务动作,不涉及任何具体平台的元素定位逻辑。以登录页为例:

// 接口完全和业务绑定,与具体平台实现无关
public interface ILoginPage
{
    void InputUsername(string username);
    void InputPassword(string password);
    void ClickLoginButton();
    bool CheckLoginSuccessVisible();
}

所有业务页面都按这个规则抽取对应接口,比如IHomePage、IOrderListPage,接口只放两个平台都支持的公共操作,平台独有的逻辑不要放到公共接口里,单独做扩展即可。

第二步:分别实现两个平台的页面对象类

为平台A、平台B分别编写独立的页面对象实现类,所有类继承对应页面的公共接口,各自维护自己的XPath、元素标识符、组件定位逻辑,方法名、入参出参严格和接口保持一致。
平台A的登录页实现示例:

// 建议按命名空间区分平台实现,比如YourTest.Pages.PA、YourTest.Pages.PB,避免类名冲突
public class PALoginPage : ILoginPage
{
    private readonly IWebDriver _driver;
    // 平台A专属元素定位规则,和平台B完全隔离
    private IWebElement UsernameInput => _driver.FindElement(By.Id("pa-user-input"));
    private IWebElement PasswordInput => _driver.FindElement(By.XPath("//div[@class='pa-pwd-wrap']/input"));
    private IWebElement LoginBtn => _driver.FindElement(By.CssSelector(".pa-login-submit"));
    private IWebElement SuccessTip => _driver.FindElement(By.ClassName("pa-welcome-banner"));

    public PALoginPage(IWebDriver driver)
    {
        _driver = driver;
    }

    public void InputUsername(string username) => UsernameInput.SendKeys(username);
    public void InputPassword(string password) => PasswordInput.SendKeys(password);
    public void ClickLoginButton() => LoginBtn.Click();
    public bool CheckLoginSuccessVisible() => SuccessTip.Displayed;
}

平台B的登录页实现逻辑完全对齐接口,仅替换为平台B的元素定位规则即可:

public class PBLoginPage : ILoginPage
{
    private readonly IWebDriver _driver;
    // 平台B专属元素定位规则
    private IWebElement UsernameInput => _driver.FindElement(By.Name("pb-account-field"));
    private IWebElement PasswordInput => _driver.FindElement(By.XPath("//section[@id='pb-login-module']//input[@type='password']"));
    private IWebElement LoginBtn => _driver.FindElement(By.Id("pb-login-submit"));
    private IWebElement SuccessTip => _driver.FindElement(By.CssSelector(".pb-user-avatar-wrap"));

    public PBLoginPage(IWebDriver driver)
    {
        _driver = driver;
    }

    public void InputUsername(string username) => UsernameInput.SendKeys(username);
    public void InputPassword(string password) => PasswordInput.SendKeys(password);
    public void ClickLoginButton() => LoginBtn.Click();
    public bool CheckLoginSuccessVisible() => SuccessTip.Displayed;
}

第三步:添加运行时平台配置

在appsettings.json中添加目标平台配置项,运行前修改配置即可切换测试平台:

{
  "TestConfig": {
    "TargetPlatform": "PA", // 可取值为PA/PB
    "PlatformBaseUrl": "对应平台的访问地址"
  }
}

编写简单的配置读取类加载配置:

public class TestConfig
{
    public string TargetPlatform { get; set; }
    public string PlatformBaseUrl { get; set; }

    public static TestConfig Load()
    {
        var config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false)
            .AddEnvironmentVariables() // 支持CI环境通过环境变量覆盖配置
            .Build();
        return config.GetSection("TestConfig").Get<TestConfig>();
    }
}

第四步:通过依赖注入动态绑定平台实现

如果使用SpecFlow编写feature文件,直接用框架自带的DI容器即可,不需要引入第三方依赖。添加全局测试钩子类,在场景执行前根据配置注册对应平台的页面对象实现:

[Binding]
public class TestStartup
{
    private readonly IObjectContainer _container;
    private readonly TestConfig _config = TestConfig.Load();

    public TestStartup(IObjectContainer container)
    {
        _container = container;
    }

    [BeforeScenario]
    public void InitTestEnv()
    {
        // 初始化浏览器驱动,可根据平台配置加差异化启动参数
        var driver = new ChromeDriver();
        driver.Manage().Window.Maximize();
        driver.Navigate().GoToUrl(_config.PlatformBaseUrl);
        _container.RegisterInstanceAs<IWebDriver>(driver);

        // 核心逻辑:根据配置注入对应平台的页面对象
        switch (_config.TargetPlatform.ToUpper())
        {
            case "PA":
                _container.RegisterTypeAs<PALoginPage, ILoginPage>();
                // 其余页面按相同格式注册,比如
                // _container.RegisterTypeAs<PAHomePage, IHomePage>();
                // _container.RegisterTypeAs<PAOrderPage, IOrderPage>();
                break;
            case "PB":
                _container.RegisterTypeAs<PBLoginPage, ILoginPage>();
                // 对应注册平台B的所有页面实现
                // _container.RegisterTypeAs<PBHomePage, IHomePage>();
                // _container.RegisterTypeAs<PBOrderPage, IOrderPage>();
                break;
            default:
                throw new ArgumentException($"配置的目标平台{_config.TargetPlatform}不支持");
        }
    }

    [AfterScenario]
    public void CleanupTestEnv()
    {
        var driver = _container.Resolve<IWebDriver>();
        driver.Quit();
        driver.Dispose();
    }
}

第五步:编写公共Step定义,完全复用测试逻辑

Step定义层只依赖公共页面对象接口,完全不感知当前运行的是哪个平台,两个平台100%共用:

[Binding]
public class LoginStepDefs
{
    private readonly ILoginPage _loginPage;

    // 构造函数注入的ILoginPage会自动根据配置加载对应平台的实现
    public LoginStepDefs(ILoginPage loginPage)
    {
        _loginPage = loginPage;
    }

    [When(@"用户输入用户名 (.*) 密码 (.*)")]
    public void InputAccountInfo(string username, string pwd)
    {
        _loginPage.InputUsername(username);
        _loginPage.InputPassword(pwd);
    }

    [When(@"点击登录按钮")]
    public void SubmitLogin()
    {
        _loginPage.ClickLoginButton();
    }

    [Then(@"登录成功状态正常展示")]
    public void VerifyLoginResult()
    {
        Assert.IsTrue(_loginPage.CheckLoginSuccessVisible());
    }
}

对应的feature文件不需要做任何平台适配,两个平台完全共用:

Feature: 用户登录
  作为平台用户
  我可以通过账号密码登录系统

Scenario: 正常账号登录成功
  When 用户输入用户名 test001 密码 Pass@123
  And 点击登录按钮
  Then 登录成功状态正常展示

落地实用提示

  • 若两个平台存在极个别流程分支差异,直接在step中读取当前平台配置走对应分支即可,不需要拆分两套step定义
  • 通用页面操作(元素显式等待、页面滚动、全局弹窗处理等)可以抽成页面对象基类,让PA、PB的页面实现都继承基类,减少重复代码
  • 后续平台迭代只需要修改对应平台的页面对象类即可,公共的feature、step逻辑完全不需要改动,维护成本很低

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 13:42:30