3

I have a website with a home page that requires a user to be authorized or logged in to see all pages of the website. If you try to access the home page, but you are not logged in, then the website sends you to a login page. The url of the page has two query parameters - some id to identify the client and the referring url. Once you login, you get access to the homepage. If you are on a page called pageABC and your session gets timed out, then you'll be shown the login page. You will go back to the previous page after logging in.

How do I model this scenario in terms of page objects?

I already have a HomePageObj which has a webdriver instance. It also has a void get() method which opens the homepage url. Should I model that page in one of the following ways or try something else ?

1 - Change void get() to return a LoginPageObj if user is redirected to login page, else return null? Then, my tests can do a login if they get null.

2 - Add a member variable called User to each page object. If void get() redirects to login page url, then use the credentials in User object to automatically do the login and open the desired url?

3 - Should I have a BasePageObj with a void get() method which works in way I mentioned in point 2? Then, all pages will extend this BasePageObj.

UPDATES :

Regarding point 3 - BasePageObj can have two variables "requiresLogin" & "doAutoLogin". If requiresLogin = true & doAutoLogin = true, then do the login automatically. Any flaws in this approach ?

Thanks.

  • Are you going to test a lot of scenarios where user times out? – Moro May 8 at 6:34
  • Page Objects should be made to ease test writing. Could you clarify the different scenarios where behavior may make Page Objects complicated? – João Farias May 8 at 13:19
  • @Moro - No. I'll probably have only 1 or 2 scenarios for timeout. – MasterJoe2 May 8 at 18:28
  • @JoãoFarias - Sorry, I don't understand. I am not sure if there are any scenarios where behavior may make POs complicated. – MasterJoe2 May 8 at 18:46
5

When I think how to design a test framework I always ask myself if the code I am writing would be easy to understand by someone else in 6 months. That is why I would suggest to keep it as simple as possible.

Since we are talking about a small amount of test cases that will deal with the timeout, you can implement timeout specifc functions inside coresponding page objects. So lets say you have a class called SomePage, you would do something like this:

public SomePage timeoutAndLogin(User user) {
    waitTillTimeout();
    new LoginPage(driver).login(user);
    return this;
    }

This way your page objects still model the behaviour of real page, you easly get back a reference to SomePage object and can utilize method chaining.

Edit

If you want a solution that will take care of login on every page, you would have to do something similar to what you mentioned in points 2 & 3. But instead of writing a void get() each page object could have a default constructor like this:

public SomePage(WebDriver driver, User user) {
    if(driver.getCurrentUrl() == LOGIN) {
        new LoginPage(driver, user).login();
    };
    this.user = user;
    this.driver = driver;
} 

So before a page is returned, we check if the URL points to login page and login back if it did (if your URL does not change, something else can be used i.e. WebElement or title).

Bare in mind that this solution can result in false positive test results, as any scenario in which a timeout should not happen and it does, will take care of the login and ignore the failure. It also adds an extra variable to each page (User) and restricts you to the same user.

The previous option would probably fail for different user, but for specific scenario a different function can be created:

public LoginPage timeoutAndLoginDifferentUser(User user) {
    waitTillTimeout();
    new LoginPage(driver).login(user);
    return new LoginPage(driver);
}

Post update edit

Any kind of boolean flag adds additional complexity layer. Two flags mean your code can end up in one of four possible states. This can lead to a lot of frustration for any person that will have to use the framework in the future, as debugging will become a pain. I had to work with Ruby Cucumber framework that used flags together with some kind of global variables - code was executing either in a nested iframe or in a new tab. It lead to lots of hours lost on debugging why we are receiving NoSuchElementException when everything seemed fine.

  • Thanks. What would your answer be if I had to test 10s to 100s of scenarios with timeout ? – MasterJoe2 May 13 at 23:04
  • I have edited my original answer, to provide a more generic solution – Moro May 14 at 6:15
  • Moro - I added an update about BasePageObj in my question. Could you please let me know how that sounds ? thanks – MasterJoe2 Jun 4 at 20:32
  • I have updated my answer – Moro Jun 5 at 7:58
1

I'd say your option 3 is the approach I would go for with a few differences. Since selenium tests are supposed to represent specific scenarios, avoid making your objects too clever. In my opinion you should know exactly what you're trying to do when a method is called.

In this case, there should be no check in the method to see if you get redirected or not. You should know where you'll end up after calling the method.

If every page when logged in does this, inheritance could be a good option. Be careful with inheritance though, it works great until you have an exception to the rule. If you have an exception then you can use composition.

Every page object you have that represents a page when you're logged in could inherit from LoggedInPage or similar.

C# example:

abstract class LoggedInPage //could extend another base page for something all pages are
{
    protected LoginPage RedirectToLoginAfterTimeout()
    {
        WaitForTimeout(); //code not shown, you get the idea.

        //assuming driver is stored in the concrete class or something
        driver.Navigate().Refresh(); //refreshing should redirect to login page
        return LoginPage(driver); //Or however you return new pages.

        //Note: I have no assertions here, usually you can assert
        //you're on a page when it's loaded, or asserts should be in your test script.
        //This method just represents that you can wait for timeout, and then redirect. 
    }
}

After this method is called, your test script can then log you in. Or you could also add another method to accept a User object to automatically log you in.

Your test script would look something like this:

...
//code to get to your start page
HomePage homePage = loginPage.Login(user);
loginPage = homePage.RedirectToLoginAfterTimeout();
//assert you're on the login page
0

Rather than having your page objects be able to handle this behaviour directly, it may be worth keeping it separate. This allows your page objects to remain simple, and have specialised code for handling this different path.

public class TimeoutHandler {
    private WebDriver driver;
    private LoginPage loginPage;

    public TimeoutHandler(WebDriver driver, LoginPage loginPage) {
        this.driver = driver;
        this.loginPage = loginPage;
    }

    public boolean checkTimeoutIsHandled(CheckableForTimeout checkable, String username, String password) {
        driver.get(checkable.getUrl());
        loginPage.logIn(username, password);
        assertEquals(checkable.getUrl(), driver.getCurrentUrl());
        assertTrue(driver.findElement(checkable.getByPresentWhenPageLoaded()).isDisplayed());
        return true;
    }
}
import org.openqa.selenium.By;

public interface CheckableForTimeout {
    String getUrl();
    By getByPresentWhenPageLoaded();
}

The CheckableForTimeout interface allows the implementing class to specify the URL that the handler should attempt to load, and where you will end up after logging in. It also allows you to specify a By locator that will be checked for after the login process.

If you want to check a page times out correctly, have its page object implement CheckableForTimeout, and then run it through the checkTimeoutIsHandled method on TimeoutHandler.

Hopefully this provides an alternative to altering your page objects too much. It still changes them a little, but there's a fairly clear separation of responsibilities.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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