2

I was wondering if anyone could assist in a Code Review on my Specflow Page Object Model code. I have included a small sample of the code I use, most of the pages follow identical patterns.

Step Definitions

    [Binding]
    public class LoginStepDefinitions
    {
        public LoginStepDefinitions(IWebDriver _driver)
        {
            driver = _driver;
        }

        static IWebDriver driver;

        [Given(@"I browse to (.*)")]
        public void GivenINavigateTo(string url)
        {
            driver.Navigate().GoToUrl(url);
        }

        [Given(@"I login as (.*?) with Password (.*)")]
        public void GivenILoginAs(string username, string password)
        {
            new LoginPom(driver, username, password);
        }

        [Then(@"I should be logged in")]
        public void ThenIShouldBeLoggedIn()
        {
            Assert.DoesNotThrow(delegate { driver.FindElement(By.Id("logoutButton")); } );

        }

        [Given(@"I go to (.*?) within the (.*?) Menu")]
        [Then(@"I should have access to (.*?) within the (.*?) Menu")]
        public void ThenIShouldHaveAccess(string submenu, string menu)
        {
            Assert.DoesNotThrow(delegate {
                new HeaderPom(driver).selectMenuByName(menu).selectSubMenuByName(submenu);
            });
        }
    }

Page Object Model

    public class LoginPom : Pages
    {
        public class LoginCredentials
        {
            public string Username { get; set; }
            public string Password { get; set; }
        }

        public static Dictionary<string, LoginCredentials> loginData = new Dictionary<string, LoginCredentials>
        {
            { "Admin",
                new LoginCredentials()
                {
                    Username= "AdminUsername",
                    Password= "AdminPassword"
                }
            }
        };
        [FindsBy(How = How.Id, Using = "ctl00_mainContent_userLogin_UserName")]
        private IWebElement UserNameTxt;
        [FindsBy(How = How.Id, Using = "ctl00_mainContent_userLogin_Password")]
        private IWebElement PasswordTxt;
        [FindsBy(How = How.Id, Using = "ctl00_mainContent_userLogin_loginButton")]
        private IWebElement LoginBtn;
        

        public LoginPom(IWebDriver driver) : base(driver)
        {
        }


        public LoginPom(IWebDriver driver, String username, String password) : base(driver)
        {
            setUsername(username).setPassword(password).submit();
        }

        public LoginPom setUsername(String username)
        {
            this.UserNameTxt.Clear();
            this.UserNameTxt.SendKeys(username);
            return this;
        }

        public LoginPom setPassword(String password)
        {
            this.PasswordTxt.Clear();
            this.PasswordTxt.SendKeys(password);
            return this;
        }

        public void submit()
        {
            this.LoginBtn.Click();
        }
    }

I'm using the ObjectContainer feature of SpecFlow to initialize my WebDriver based on the tag(s) applied to the test(s).

        [BeforeScenario("Firefox")]
        public void InitializeFirefoxWebDriver()
        {
            DbAccess entities = new DbAccess();
            DesiredCapabilities capability = DesiredCapabilities.Firefox();
            capability.Platform = new Platform(PlatformType.Any);
            launchWebDriver(capability);
            objectContainer.RegisterInstanceAs<DbAccess>(entities);
        }


        public void launchWebDriver(DesiredCapabilities capability)
        {
            string gridUrl = "http://127.0.0.1:4444/wd/hub/";
            ScenarioInfo si = ScenarioContext.Current.ScenarioInfo;
            if (si.Tags.Any(x => x.StartsWith("GridUrl=")))
            {
                gridUrl = ConvertToString(si.Tags.FirstOrDefault(x => x.StartsWith("GridUrl=")).Split('=')[1]);
            }
            var webDriver = new RemoteWebDriver(new Uri(gridUrl), capability);
            webDriver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(15));
            objectContainer.RegisterInstanceAs<IWebDriver>(webDriver);
        }

I'm currently in the process of switching companies so I'm wanting to make certain, that the code is as easy to interpret and maintain as possible.

Any feedback is greatly appreciated.

2
  • I would recommend that you should stay away from hardcoding anything, URLs, credentials, elements access methods should be stored in datasheet or something like static variables class. Commented May 5, 2017 at 6:05
  • I disagree with the philosophy of avoiding hardcoding everything. The discussion is too large for comments but there is benefits and drawbacks to both sides and like everything else in life there is places where it should be done and places where it should not be done.
    – Paul Muir
    Commented May 25, 2017 at 1:41

1 Answer 1

2

My very personal opinions, please take them as a grain of salt

  • In public class LoginStepDefinitions public LoginStepDefinitions(IWebDriver _driver) { driver = _driver; } static IWebDriver driver;

    why do you declare driver after it is used this class constructor? It may confuse a reader a bit.

  • You are using FindsBy(How = How.Id,, do IDs not change over time? It is not uncommon for the developers to chanage IDs without telling anyone.

  • This function has three input arguments,

    public LoginPom(IWebDriver driver, String username, String password) : base(driver) { setUsername(username).setPassword(password).submit(); }

    Is it possible to reduce it to one?

1
  • Great catch on the declaration after constructor, my IDs rarely change (They haven't in the 6 months since I built this) and while I could change that override of the constructor to take a driver and a LoginCredentials object it would just push the processing up the ladder into the StepDefinitions. I will however make another override that does this as well for the one off tests that I have elsewhere. Thank you for the feedback, greatly appreciated!
    – Paul Muir
    Commented Oct 31, 2016 at 19:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.