2

In the web application i am trying to automate there is a Login page and a Secure page.

When user enters valid credentials in Login page s/he is navigated to Secure page with a success message. If the credentials are invalid s/he would stay in the same page with an error message.

I have created two page objects to represent them LoginPage and Secure Page.

Login Page

public class LoginPage extends BasePage
{
  public void loginWith(String username,String password)
  {
    enter(usernameLocator, username);
    enter(passwordLocator,password);
    clickOn(loginButton);
 }
}

Secure Page

 public class SecurePage extends BasePage
 {
    public void logout()
    {
        if(isDisplayed(logoutButton))
        {
            clickOn(logoutButton);
        }
        else
        {
            System.out.println("Log out button is not displayed");
        }
        return this;
    }      
 }

Test Class

public class LoginTests extends BaseTest
{
    @Test(dataProvider="TestData", dataProviderClass=TestDataProvider.class)
    public void validUserLoginTest(String username, String password)
    {
      LoginPage login = new LoginPage(driver);
      login.loginWith(username, password);
    }
}

Now, I want make modifications to my loginWith() method so that I could return respective page object based on the credentials entered. (i.e, if valid credentials -> Secure Page, invalid credentials -> Login Page).

Can you please suggest how could I do achieve that?

One solution I thought is to create two methods in my login page like loginWithValidCrendentials(), loginWithInvalidCrendentials().

But, I don't want to create two methods which basically does the same action(but would produce different results because of their method parameters).

1 Answer 1

2

The login action could always return the securePage as this is the location you would expect it to end up if a login was successful. In the case you use invalid credentials you best use the old pageObject again in your test to verify you are still on the same page.

Here some example pseudo-code:

class LoginPage {
  // locators
  usernameLocator = By.id("username");
  passwordLocator = By.id("password");
  loginButton = By.id("loginBtn");

  // actions
  loginWith(username, password) {
    enter(usernameLocator, username);
    enter(passwordLocator, password);
    clickOn(loginButton);
    return new SecurePage(driver);
 }
}

class SecurePage {
  // locators
  logoutButton = By.id("logoutBtn");

  // logout
  logout() {
    clickOn(logoutButton);
    return new LoginPage(driver);
  }
}

And two tests:

class LoginTests {
    @Test(dataProvider="TestDataValidUsers")
    validUserLoginTest(username, password) {
      // arrange
      LoginPage login = new LoginPage(driver);
      // act
      SecurePage page = login.loginWith(username, password);
      // assert
      assert.False(login.loginButton.isDisplayed());
      page.logout();
      assert.True(login.loginButton.isDisplayed());
    }

    @Test(dataProvider="TestDataInvalidUsers")
    invalidUserLoginTest(username, password) {
      // arrange
      LoginPage login = new LoginPage(driver);
      // act
      SecurePage page = login.loginWith(username, password);
      // assert
      assert.False(page.logoutButton.isDisplayed());
      assert.True(login.loginButton.isDisplayed());
    }

}

Some points:

  • Now the test validate login and logout, which I would split into separate tests, but thats a different thing.
  • You should be able to use the pageObjects even if the page was not rendered. Your example code does an if in the code. That is essential an assert or logic in the pageObject. Which is not advised as it should describe just the locators and the actions. Not if those are working correctly, handle that in your tests. (Some are pro assertions in pageObjects, some are against, decide for yourself, I am against ;-)
  • Don't try to put the invalid and valid users tests in to a single tests. It is perfectly fine to make multiple tests. Or for that matter multiple functions with slightly different effects if it enhances readability and maintainability of the tests.
  • An action leading to a new location should always return its happy path location. Try to minimise on the logic in the pageObjects.
  • If the pageObject cannot find the element it will throw an exception, there is not need to add your own error handling. Do add a wait for each element you try to interact with.

But here an example where login with a different users would result into two different landing pages.

class LoginPage {
  // locators
  ...
  // actions
  loginAdmin(username, password) {
    login(username, password);
    return new AdminPage(driver);
  }

  loginUser(username, password) {
    login(username, password);
    return new SecurePage(driver);
  }

  loginWith(username, password) {
    if(username === admin) {
      login()
      return new AdminPage();
    } else {
      login()
      return new SecurePage();
    }
  }

  login(username, password) {
    enter(usernameLocator, username);
    enter(passwordLocator, password);
    clickOn(loginButton);
  }

}

Either use the two functions option or the one with the ifs. I would prefer the two separate functions as the intention is more clear making the tests better readable.

3
  • Hi @Neils van Reijmersdal, great answer. 'An action leading..' point cleared my confusion. But, i have a doubt, If i were to use the 'if/else' solution provided in your example what should be the return type of the loginWith() method?. If answer is to use a generics then how could i do it?. Thanks in advance.
    – the_coder
    Apr 1, 2017 at 16:12
  • Sorry for the late reply, somehow missed it. Personally I do not really care about types in most POM code. So in the if/else case just make it work. Questions about generics, which possible could solve this is better asked on stackoverflow.com instead as it is more a programming question. But guess you solved it, good job! :) Maybe place a comment for others what your final design choose became and why. Apr 7, 2017 at 12:08
  • Thanks @Neils, I have opted to go with two methods solution, like loginwithValidCre(uname,pwd), loginwithInvalidCre(uname,pwd). both the methods would call login() method which does login functionality only and respective valid/invalid credentials methods would return the respective page (Secure/Login) .
    – the_coder
    Apr 8, 2017 at 2:31

Your Answer

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

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