不必要的 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 的需求。
目前正在使用 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 的需求。