【问题标题】:PageFactory pattern - extend IWebElement C#PageFactory 模式 - 扩展 IWebElement C#
【发布时间】:2016-05-12 14:54:14
【问题描述】:

我开始在 Selenium .NET 驱动程序上实现 PageFactory 设计模式。我有一个名为示例“ButtonControl”的 Page 对象类,我希望将其视为 IWebElement。

默认看起来像:

[FindsBy(How = How.CssSelector, Using = "someSelector")]
public IWebElement button1;

我真正想要的是:

// in the page object:
[FindsBy(How = How.CssSelector, Using = "someSelector")]
public ButtonControl button1;

// in test code:
page.button1.Click();

所以我需要的是……我不知道。也许自定义工厂将创建此页面对象?

有什么想法吗?

【问题讨论】:

  • 您似乎误解了 PageFactory 模式的使用。为什么需要实现这些奇怪的东西?

标签: c# selenium webdriver automated-tests pageobjects


【解决方案1】:

Page object model 应该只保留 UI 元素的地图,仅此而已。在许多文章中,您会看到建议放置与页面交互的方法 - 但这是错误的。

/**
 * Page Object encapsulates the Sign-in page.
 */
public class LoginPage{

        private final WebDriver driver;

    public LoginPage(WebDriver driver) {
    this.driver = driver;

    // Check that we're on the right page.
    if (!"Login".equals(driver.getTitle())) {
        // Alternatively, we could navigate to the login page, perhaps logging out first
        throw new IllegalStateException("This is not the login page");
    }
  }

       // The login page contains several HTML elements that will be represented as WebElements.
       // The locators for these elements should only be defined once.
       By usernameLocator = By.id("username");
       By passwordLocator = By.id("passwd");
       By loginButtonLocator = By.id("login");

        /**
         * Login as valid user
         *
         * @param userName
         * @param password
         * @return HomePage object
         */
        public LoginPage loginValidUser(String userName, String password) {

                driver.findElement(usernameLocator).sendKeys(userName);
                driver.findElement(usernameLocator).sendKeys(password);
                driver.findElement(loginButtonLocator).click();

                return new HomePage(selenium);
        }
}

这违反了SRP,在上面的例子中你有两个改变的理由——如果定位器改变了,如果流改变了。恕我直言,大多数时候是智能测试和智能页面对象之间的权衡问题。但是你可以实现你的 Action 类来处理与页面的交互。

【讨论】:

    【解决方案2】:

    PageObject 的想法真的很酷。 :)

    您可以使用库:https://github.com/DotNetSeleniumTools/DotNetSeleniumExtras 扩展类https://github.com/DotNetSeleniumTools/DotNetSeleniumExtras/blob/master/src/PageObjects/DefaultPageObjectMemberDecorator.cs

    你的班级应该是这样的:

    class CustomPageObjectMemberDecorator : DefaultPageObjectMemberDecorator, IPageObjectMemberDecorator
    {
        private BaseDriver driver;
        public CustomPageObjectMemberDecorator(BaseDriver driver) => this.driver = driver;
    
        public new object Decorate(MemberInfo member, IElementLocator locator)
        {
            FieldInfo field = member as FieldInfo;
            PropertyInfo property = member as PropertyInfo;
            Type targetType = null;
            if (field != null)
            {
                targetType = field.FieldType;
            }
    
            bool hasPropertySet = false;
            if (property != null)
            {
                hasPropertySet = property.CanWrite;
                targetType = property.PropertyType;
            }
    
            var genericType = targetType.GetGenericArguments().FirstOrDefault();
    
    
            if (field == null & (property == null || !hasPropertySet))
            {
                return null;
            }
            // IList<IWebElement>
            if (targetType == typeof(IList<IWebElement>))
            {
                return base.Decorate(member, locator);
            }
            // IWebElement
            else if (targetType == typeof(IWebElement))
            {
                return base.Decorate(member, locator);
            }
    
            // BaseElement and childs
            else if (typeof(BaseElement).IsAssignableFrom(targetType))
            {
                var bys = CreateLocatorList(member);
                var cache = ShouldCacheLookup(member);
                IWebElement webElement = (IWebElement)WebElementProxy.CreateProxy(locator, bys, cache);
                return GetElement(targetType, webElement, driver, field.Name);
            }
    
            // IList<BaseElement> and childs
            else if (targetType.GetInterfaces().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>)) != null)
            {
                Type elementOfListTargetType = targetType.GetGenericArguments()[0];
                if (typeof(BaseElement).IsAssignableFrom(elementOfListTargetType))
                {
                    var cache = ShouldCacheLookup(member);
                    IList<By> bys = CreateLocatorList(member);
                    if (bys.Count > 0)
                    {
                        return CustomElementListProxy.CreateProxy(driver, field.Name, elementOfListTargetType, locator, bys, cache);
                    }
                    else
                    {
                        return null;
                    }
                }
                else
                {
                    return null;
                }
            }
    
            else
            {
                return null;
            }
        }
    
        private Element GetElement(Type type, IWebElement webElement, BaseDriver baseDriver, string Name)
        {
            return (Element)type
                .GetConstructor(new[] { typeof(IWebElement), typeof(BaseDriver), typeof(string) })
                .Invoke(new object[] { webElement, baseDriver, Name });
        }
    }
    

    你还需要初始化你的页面:

    public BasePage(BaseDriver Driver) : base(Driver)
        {
            IPageObjectMemberDecorator pageObjectMemberDecorator = new CustomPageObjectMemberDecorator(_driver);
            PageFactory.InitElements(this, _driver.RetryingElementLocator, pageObjectMemberDecorator);
        }
    

    您的 PageObject 将如下所示:

    public class LoginPage : BasePage, ILoad
    {
        [FindsBy(How = How.XPath, Using = "//*[@class = 'linkButtonFixedHeader office-signIn']")] private Button SignIn;
        [FindsBy(How = How.XPath, Using = "//input[@type = 'email']")] private TextInputField Email;
        [FindsBy(How = How.XPath, Using = "//input[@type = 'submit']")] private Button Submit;
        [FindsBy(How = How.XPath, Using = "//input[@type = 'password']")] private TextInputField Password;
    
        public LoginPage() : base(new BaseDriver(), "https://outlook.live.com/")
        {
        }
    
        public bool IsLoaded() =>
            SignIn.IsVisible() &&
            Email.IsVisible() &&
            Submit.IsVisible();
    
        public MainMailPage PositiveLogin(User user)
        {
            SignIn.Click();
            Email.SendString(user.Login);
            Submit.Click();
            Password.SendString(user.Pass);
            Submit.Click();
            return Page<MainMailPage>();
        }
    }
    

    你也可以扩展 FindByAttribute 和 PageObject 将被简化:

    public class LoginPage : BasePage, ILoad
    {
        [XPath("//*[@class = 'linkButtonFixedHeader office-signIn']")] private Button SignIn;
        [XPath("//input[@type = 'email']")] private TextInputField Email;
        [XPath("//input[@type = 'submit']")] private Button Submit;
        [XPath("//input[@type = 'password']")] private TextInputField Password;
    
        public LoginPage() : base(new BaseDriver(), "https://outlook.live.com/")
        {
        }
    
        public bool IsLoaded() =>
            SignIn.IsVisible() &&
            Email.IsVisible() &&
            Submit.IsVisible();
    
        public MainMailPage PositiveLogin(User user)
        {
            SignIn.Click();
            Email.SendString(user.Login);
            Submit.Click();
            Password.SendString(user.Pass);
            Submit.Click();
            return Page<MainMailPage>();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-10
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 2012-12-19
      • 1970-01-01
      • 1970-01-01
      • 2012-03-23
      • 2010-09-05
      相关资源
      最近更新 更多