不必要的 class 实例化重复

Unnecessary class instantiation repetition

目前正在使用 C# 和 SpecFlow 构建自动化框架。我目前的一个抱怨是我实例化 class 以访问方法或 Web 元素的次数。

下面是class的一部分,我在一个单独的class方法中实例化

public class UniversalSelectors : BasePage
{
    public  UniversalSelectors(IObjectContainer container): base(container)  { }

    //iFrame
    public IWebElement iFrame => Driver.FindElement(By.Id("content"));
    //Nav Bar Elements
    public void ClickAdministration()
    {
        Driver.FindElement(By.XPath("//a[@id='administration-menu-item']"));
    }
    public void ClickDevicesMenu()
    {
        Driver.FindElement(By.XPath("//*[@id='manage-devices-menu-item']"));
    }
    public void ClickRemoteMonitoring()
    {
        Driver.FindElement(By.Id("manage-monitoring-menu-item"));
    }
    public void ClickSystemUsers()
    {
        Driver.FindElement(By.XPath("//*[@id='manage-users-menu-header']"));
    }
    //Quick Search Elements
    public IWebElement Quick_Search => Driver.FindElement(By.XPath("//*[@class='pSearch pButton']"));
    public IWebElement Quick_Search_Box => Driver.FindElement(By.XPath("//*[@class='qsbox']"));
    public IWebElement Quick_Search_Button => Driver.FindElement(By.XPath("//*[@class='btn btn-sm btn-success flexigrid-search-button']"));
    public IWebElement Quick_Clear_Button => Driver.FindElement(By.XPath("//*[@class='btn btn-sm btn-success flexigrid-clear-button']"));

在下面你可以看到我是如何在不同 class 的每个方法中实例化的。但我想知道的是,这是必要的还是有更好的解决方法?

public void NavigateToRoles()
{
    var universalselectors = new UniversalSelectors(_container);

    universalselectors.ClickAdministration();
    Thread.Sleep(1000);
    universalselectors.ClickSystemUsers();
    Thread.Sleep(1000);
    Roles.Click();
    Thread.Sleep(2000);
}
public void CreateBlankRole()
{
    var universalselectors = new UniversalSelectors(_container);

    Driver.SwitchTo().DefaultContent();
    Driver.SwitchTo().Frame(universalselectors.iFrame);
    universalselectors.ClickNewRecord();
    Thread.Sleep(2000);
    universalselectors.ClickSaveRecord();
    Thread.Sleep(2000);
}

您可以使用 dependency injection (see also constructor injection). Within the confines of SpecFlow, this involves creating a new instance of UniversalSelectors in a SpecFlow hook 消除初始化相同类型的新对象并将其注册到 SpecFlow 对象容器。接下来,任何需要页面模型的步骤定义 class 都应包含一个 UniversalSelectors 对象作为其构造函数参数之一,并将其​​与 Web 驱动程序对象一起传递给页面模型的构造函数。

向 SpecFlow 依赖注入框架(对象容器)注册 Web Driver 和 UniversalSelector 对象:

[Binding]
public class SpecFlowHooks
{
    private readonly IObjectContainer container;

    public SpecFlowHooks(IObjectContainer container)
    {
        this.container = container;
    }

    [BeforeScenario]
    public void BeforeScenario()
    {
        var driver = new ChromeDriver() | FirefoxDriver() // or whatever web driver you use

        // Configure the web driver

        var universalSelectors = new UniversalSelectors(driver);

        container.RegisterInstanceAs<IWebDriver>(driver);
        container.RegisterInstanceAs(universalSelectors);
    }
}

现在,将 UniversalSelectors 对象添加到页面模型的构造函数参数中:

public class RolesPageModel
{
    private readonly IWebDriver Driver;
    private readonly UniversalSelectors universalSelectors;

    // other properties

    public RolesPageModel(IWebDriver driver, UniversalSelectors universalSelectors)
    {
        Driver = driver;
        this.universalSelectors = universalSelectors;
    }

    public void NavigateToRoles()
    {
        universalselectors.ClickAdministration();
        Thread.Sleep(1000);
        universalselectors.ClickSystemUsers();
        Thread.Sleep(1000);
        Roles.Click();
        Thread.Sleep(2000);
    }

    public void CreateBlankRole()
    {
        Driver.SwitchTo().DefaultContent();
        Driver.SwitchTo().Frame(universalselectors.iFrame);
        universalselectors.ClickNewRecord();
        Thread.Sleep(2000);
        universalselectors.ClickSaveRecord();
        Thread.Sleep(2000);
    }
}

将事物结合在一起的粘合剂在于您的步骤定义,您还需要在其中为这些构造函数声明 UniversalSelectors 参数:

[Binding]
public class RoleSteps
{
    private readonly RolesPageModel roles;

    public Roleteps(IWebDriver driver, UniversalSelectors universalSelectors)
    {
        roles = new RolesPageModel(driver, universalselectors);
    }

    [When(@"I create a blank role")]
    public void WhenICreateABlankRole()
    {
        roles.CreateBlankRole();
    }
}

附录

在我看来,真正的问题是 UniversalSelectors class 本身。 "selectors"(或 Selenium 术语中的 "locators")可以在使用定位器的页面模型中轻松声明。事实上,这是首选方法。页面具有公共元素,如导航菜单和搜索栏。这些公共页面元素应该是它们自己的页面模型。组合比继承在这里更有用:

public class RolesPageModel
{
    private readonly IWebDriver Driver;
    private readonly NavigationBarPageModel navBar;
    private readonly SiteSearchBarPageModel searchBar;

    // other properties

    public RolesPageModel(IWebDriver driver)
    {
        this.driver = driver;
        navBar = new NavigationBarPageModel(driver);
        searchBar = new SiteSearchBarPageModel(driver);
    }

    public void NavigateToRoles()
    {
        navBar.GoToRoles();
    }

这简化了所有页面模型的依赖关系,并完全消除了对 UniversalSelectors 的需求。