Discussion:
[Cucumber] [JVM] [Selenium] [DI] Using PageObject pattern with cukes and sharing page objects between stepdefs
(too old to reply)
oiale
2017-10-15 22:08:21 UTC
Permalink
Raw Message
Hello,
This is going to be a bit long, sorry about that.
Is code highlight syntax lagging (b/c of spellcheck) only in my browser
(fox 56) ?

For my tests, I am following the PageObject pattern.
So far I have about 4-5 pages to test, so I have as many page objects + 1
object for a navbar that exists in almost every page.
I am using SharedDriver from cucumber-jvm example and PicoContainer as a DI
framework.

Here are parts of page object snippets (I was kind of going by the pattern
described in selenium wiki on github).

public class LoginPage
{
private final SharedDriver driver;

@FindBy(id="username")
private WebElement usernameBox;
...
public LoginPage(SharedDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
...
//some methods here that do different webdriver calls with waits and
such
...
public MainPage clickLogin() {
loginButton.click();

return new MainPage(driver);
}
}

public class MainPage
{
private final SharedDriver driver;

private MenuBar menuBar; // this exists in every page except login
...

public MainPage(SharedDriver driver) {
this.driver = driver;
this.menuBar = new MenuBar(driver);
PageFactory.initElements(driver, this);
PageFactory.initElements(driver, menuBar);
}
...
//and so on
}



The structure of my pages is as following:
Login -> Main
-> A -> C
-> B -> C
Which means that page C can be accessed through A or B.

I also use cucumber and here is where all the questions come.
I want to separate certain steps that are most common for every scenario.
For example: login

I have a step that looks like: Given User is logged in as X:Y
which implementation I put in the file CommonSteps.java

public class CommonSteps
{
@Inject
private SharedDriver driver;

private LoginPage loginPage = null;
private MainPage mainPage = null;

public MainPage getMainPage() {
return this.mainPage;
}

@Given(...)
public void user_logged_in_as_X_Y (String username, String password) {
loginPage = new LoginPage(driver);
loginPage.loadPage();
assertTrue("msg", loginPage.isPageLoaded());

loginPage.enterUsername(username);
...
mainPage = loginPage.clickLogin();
}
}



Then I inject CommonSteps into other step definition classes that need the
login functionality in their tests.

public class MainPageSteps
{
private MainPage mainPage = null;
private A aPage = null;

public MainPageSteps(CommonSteps commonSteps) {
this.mainPage = commonSteps.getMainPage();
}

@When(...)
public void get_to_a()
{
assertNotNull(mainPage);
assertTrue(mainPage.isPageLoaded());

aPage = mainPage.goToA();
}
}



*So here comes question 1*: Is it a good way to write step definitions? I
most of the examples online, people just inject the webdriver and do a lot
of selenium specific code without covering it with PageObject pattern.

Here is another example, steps for page C which is a page accessible from
pages A and B.

public class CPageSteps
{
private A aPage = null;
private B bPage = null;

public CPageSteps(ASteps a, BSteps b) {
aPage = a.getPageA();
bPage = b.getPageB();
}
//not good because steps for page c should not care about pages A or B
and their steps
}

// or

public class CPageSteps
{
private C cPage = null;

public CPageSteps(ASteps a, BSteps b) {
// where do I get the cPage from ???
}
}




I have though about the alternative: only injecting the SharedDriver and
reinstantiating page objects in every step.
Like:

public class CPageSteps
{
@Inject
private SharedDriver driver;

@When(...)
public void do_something_at_page_c() {
C cPage = new C();
assertTrue("msg", cPage.isPageLoaded());

... //do something here
}
}



I personally don't like this because it kind of breaks the whole PageObject
pattern. With this approach:
1) I don't have to return page objects from methods of page objects
public MainPage clickLogin() {
loginButton.click();

//return new MainPage(driver); //this is not needed anymore
}



2) In each step definition I have to reinstantiate the page object and do a
check on whether the page is loaded, and I don't like that either.

*Main question*: what am I doing wrong and how would I go about the whole
test model?

There is a side note: there would be interesting to use another pattern
they introduce, which is LoadableComponent, but everything becomes even
weirder especially with how they do assertions in page objects isLoaded()
method.

PS: another side question: is there a way to run tagged features before
untagged with @CucumberOptions or would I have to utilize different runner
classes? There are some tests that need to be executed before everyone else
(like log-in which is used in every feature). Also, is there a way to fail
features that depend on tagged features (if log-in test fails, we can't run
any main tests obviously) ?
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Marit van Dijk
2017-10-17 04:43:33 UTC
Permalink
Raw Message
Some thoughts on Page Object: https://martinfowler.com/bliki/PageObject.html
Main point I remember is that it's a bit of a misnomer as a PageObject
doesn't necessarily have to model a Page, but could also model a relevant
component of a page (like your example of the nav bar).

We also use PageObject Pattern, but we don't return a page each time. So
I'm not sure why you feel this is necessary?

How to structure them: It depends ;)
However you structure them, should make sense to you & your team. So you
can probably best decide that.
That said, I would personally prefer to structure steps more in terms of
functionality than the actual page.
The intent of Cucumber / BDD is to describe *behaviour*, not
implementation. Which is also why some don't like to use PageObject Pattern
with Cucumber, as PageObject is very focussed on implementation. On the
other hand, I do find it helpful for interaction with the UI (if you must).

Another general consideration:
Do you have to go through the UI to test your application? As UI tests are
generally slow and brittle..

If so, is there any way to go directly to the desired page with the desired
user (loging / access rights / whatever)?
We do this in our application by opening the page directly and mocking the
calls to the external services that determine user rights.


For more discussion on structuring your tests, please also visit Slack :)

PS. Interesting side question; that might be a whole discussion on it's
own...
Post by oiale
Hello,
This is going to be a bit long, sorry about that.
Is code highlight syntax lagging (b/c of spellcheck) only in my browser
(fox 56) ?
For my tests, I am following the PageObject pattern.
So far I have about 4-5 pages to test, so I have as many page objects + 1
object for a navbar that exists in almost every page.
I am using SharedDriver from cucumber-jvm example and PicoContainer as a
DI framework.
Here are parts of page object snippets (I was kind of going by the pattern
described in selenium wiki on github).
public class LoginPage
{
private final SharedDriver driver;
@FindBy(id="username")
private WebElement usernameBox;
...
public LoginPage(SharedDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
...
//some methods here that do different webdriver calls with waits and
such
...
public MainPage clickLogin() {
loginButton.click();
return new MainPage(driver);
}
}
public class MainPage
{
private final SharedDriver driver;
private MenuBar menuBar; // this exists in every page except login
...
public MainPage(SharedDriver driver) {
this.driver = driver;
this.menuBar = new MenuBar(driver);
PageFactory.initElements(driver, this);
PageFactory.initElements(driver, menuBar);
}
...
//and so on
}
Login -> Main
-> A -> C
-> B -> C
Which means that page C can be accessed through A or B.
I also use cucumber and here is where all the questions come.
I want to separate certain steps that are most common for every scenario.
For example: login
I have a step that looks like: Given User is logged in as X:Y
which implementation I put in the file CommonSteps.java
public class CommonSteps
{
@Inject
private SharedDriver driver;
private LoginPage loginPage = null;
private MainPage mainPage = null;
public MainPage getMainPage() {
return this.mainPage;
}
@Given(...)
public void user_logged_in_as_X_Y (String username, String password) {
loginPage = new LoginPage(driver);
loginPage.loadPage();
assertTrue("msg", loginPage.isPageLoaded());
loginPage.enterUsername(username);
...
mainPage = loginPage.clickLogin();
}
}
Then I inject CommonSteps into other step definition classes that need the
login functionality in their tests.
public class MainPageSteps
{
private MainPage mainPage = null;
private A aPage = null;
public MainPageSteps(CommonSteps commonSteps) {
this.mainPage = commonSteps.getMainPage();
}
@When(...)
public void get_to_a()
{
assertNotNull(mainPage);
assertTrue(mainPage.isPageLoaded());
aPage = mainPage.goToA();
}
}
*So here comes question 1*: Is it a good way to write step definitions? I
most of the examples online, people just inject the webdriver and do a lot
of selenium specific code without covering it with PageObject pattern.
Here is another example, steps for page C which is a page accessible from
pages A and B.
public class CPageSteps
{
private A aPage = null;
private B bPage = null;
public CPageSteps(ASteps a, BSteps b) {
aPage = a.getPageA();
bPage = b.getPageB();
}
//not good because steps for page c should not care about pages A or
B and their steps
}
// or
public class CPageSteps
{
private C cPage = null;
public CPageSteps(ASteps a, BSteps b) {
// where do I get the cPage from ???
}
}
I have though about the alternative: only injecting the SharedDriver and
reinstantiating page objects in every step.
public class CPageSteps
{
@Inject
private SharedDriver driver;
@When(...)
public void do_something_at_page_c() {
C cPage = new C();
assertTrue("msg", cPage.isPageLoaded());
... //do something here
}
}
I personally don't like this because it kind of breaks the whole
1) I don't have to return page objects from methods of page objects
public MainPage clickLogin() {
loginButton.click();
//return new MainPage(driver); //this is not needed anymore
}
2) In each step definition I have to reinstantiate the page object and do
a check on whether the page is loaded, and I don't like that either.
*Main question*: what am I doing wrong and how would I go about the whole
test model?
There is a side note: there would be interesting to use another pattern
they introduce, which is LoadableComponent, but everything becomes even
weirder especially with how they do assertions in page objects isLoaded()
method.
PS: another side question: is there a way to run tagged features before
classes? There are some tests that need to be executed before everyone else
(like log-in which is used in every feature). Also, is there a way to fail
features that depend on tagged features (if log-in test fails, we can't run
any main tests obviously) ?
--
Posting rules: http://cukes.info/posting-rules.html
---
You received this message because you are subscribed to the Google Groups "Cukes" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cukes+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...