PageFactory模式 - 扩展IWebElement C#

时间:2016-02-03 12:03:27

标签: c# selenium webdriver automated-tests pageobjects

我开始在Selenium .NET驱动程序上实现PageFactory设计模式。我有一个名为example“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();

所以我需要的是......我不知道。也许自定义工厂会创建这个页面对象?

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

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 类,该类将负责与页面的交互。

答案 1 :(得分:0)

对于PageObject来说,这确实是一个很棒的主意。 :)

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

您的班级选择应该是这样的:

import re
def count_substring(string, sub_string):
    count=len(re.findall('(?='+sub_string+')', string))
    return count

还需要初始化页面:

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 });
    }
}

您的PageObject将如下所示:

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

还可以扩展FindByAttribute,而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>();
    }
}