C# PageFactory – Why I don’t use properties.

C# PageFactory – Why I don’t use properties.

This entry is part 2 of 5 in the series Building a .Net Core PageFactory Framework

In my most popular post ‘C# and the disappearing PageFactory – My next steps in Selenium testing’ I gave Jim Evans suggested approach of using properties to return the response from your page controllers. I have never used this approach however, and before I start discussing how I do build them, I thought I should explain why.

Who is this code maintained by?

In general, the idea of using annotations to allow fields to be used for accessing WebDriver calls is really a convenience hack. It does make life a lot simpler for someone with little coding to start up, write maintainable code and understand the structure of classes by relating them to the pages and html blocks that they are trying to interact.

However I am very passionate that test automation code should not be something separate from the application code. It should be checked into the same repository and the tests run automatically as part of the Continuous Integration (CI) pipeline.

Most importantly, the first person responsible for fixing a broken test should be the developer who changed the application. Your test may be written by a test automation specialist, and of course if the application is completely changed you will probably also schedule them to write the new classes, but day to day maintenance should be the responsibility of the application developers.

‘You broke it, you fix it.’

For this reason, I believe that test code needs to be reviewed by the developers that will maintain it, and follow the same coding standards and guidelines that they follow.

So what’s the problem with properties returning IWebElements?

Microsoft’s own design guidelines on properties give the following advice:

X AVOID throwing exceptions from property getters.
Property getters should be simple operations and should not have any preconditions. If a getter can throw an exception, it should probably be redesigned to be a method. Notice that this rule does not apply to indexers, where we do expect exceptions as a result of validating the arguments.

https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/property?redirectedfrom=MSDN

Properties are really intended to be a lightweight means of returning the value from a field. C# developers will not expect a property to go off and make an external api call that might throw exceptions.

Don’t get me wrong, they work just fine, but it is not the behaviour that is expected of a property.

Ideally it should be an async method that indicates this is a ‘slow’ call that is dependent on an external call. Unfortunately, WebDriver’s methods are not natively async, and in reality, browser actions are essentialy synchronous so I’ll live with that, but a method it definitely should be.

In that post I gave the following sample classes: (slightly tweaked)

Page

    public class FooBarPage : IFooBarPage
    {
        private readonly IWebDriver driver;
        
        public FooBarPage(IWebDriver driver) {
            this.driver = driver;
        }

        private IWebElement FooElement
        {
            get
            {
                return this.driver.FindElement(By.Id("foo"));
            }
        }

        private IWebElement BarElement
        {
            get
            {
                return this.driver.FindElement(By.Id("bar"));
            }
        }

        public IFooBarPage ClickFoo()
        {
            FooElement.Click();
            return this;
        }
        
        public string getBarText() 
        {
            return BarElement.Text;
        }
    }

Steps

    public class FooBarSteps
    {
        private IFooBarPage fooBarPage;

        public FooBarSteps(IFooBarPage fooBarPage)
        {
            this.fooBarPage = fooBarPage;
        }

        public FooBarSteps AssertBarText(string barText)
        {
            fooBarPage.GetBarText()
                .Should()
                .Be("bar");
            return this;
        }
        
        public void clickFoo()
        {
            fooBarPage.ClickFoo();
        }
    }

Refactored Page

    public class FooBarPageNew : IFooBarPage
    {
        private const string BarElementCssSelector = "#bar";
        
        private readonly IWebDriver driver;

        public FooBarPageNew(IWebDriver driver) {
            this.driver = driver;
        }
        
        private IWebElement GetFooElement()
        {
            return this.driver.FindElement(By.Id("foo")); 
        }
        
        private IWebElement GetBarElement() 
        {
            return this.driver.FindElement(By.CssSelector(BarElementCssSelector)); 
        }
        
        public void ClickFoo() 
        { 
            GetFooElement().Click();
        }
        
        public string GetBarText() 
        { 
            return GetBarElement().Text; 
        }
    }

Edit: 18/10/2019

Fixed non compiling refactored page code and corrected some typos. Thanks to Jeff Pugeot for raising the problem in the comments.

Progress made:

  • A different approach to making WebDriver calls in a PageFactory style requiring less lines of code

Lessons learnt:

  • Using default PageFactory style properties (or even the original fields) is not how regular C# developers would expect these members (fields / properties) to behave.
  • Moving to using methods generates code that would be more familiar to application developers maintaining your test code.

A reminder:

If you want to ask me a question, Twitter is undoubtedly the fastest place to get a response: My Username is @AlexanderOnTest so I am easy to find. My DMs are always open for questions, and I publicise my new blog posts there too.

Series Navigation<< C# and the disappearing PageFactory – My next steps in Selenium testingC# PageFactory – Starting with Controllers >>

6 thoughts on “C# PageFactory – Why I don’t use properties.

  1. Which .NET Core version are you running? How on earth are you able to define those IWebElement methods without an accessor?

  2. Completely agree properties aren’t a place to be throwing unhandled exceptions, really.
    I’m curious – the refactored page – does this compile? It looks like you’re missing parenthesis on your methods:
    private IWebElement BarElement
    should be
    private IWebElement BarElement() shouldn’t it, or am I being dumb?

  3. Good spot Jeff you are indeed correct and I have ammended the post to reflect that. Also being methods I renamed them to GetFooElement() etc as more appropriate for a getter method

    Thanks for that: I blame the pressure of getting out a post by the end of month deadline. Corrections and requests for clarity are always welcome.

    Alexander

  4. Thank you also Alen. Sorry I didn’t see your comment earlier. It was indeed incorrect. I hope my corrections make a little more sense.

    Alexander

  5. Does your blog have a contact page? I’m having trouble locating it but, I’d like to shoot you an e-mail. I’ve got some suggestions for your blog you might be interested in hearing. Either way, great site and I look forward to seeing it improve over time.|

  6. Hi Delfina,

    Apologies, my standard contact comment was missed from this post.

    I would prefer questions via my twitter account @AlexanderOnTest It is bay far the most likely to get a response other than comments here.

    Regards
    Alexander

Comments are closed.

Comments are closed.