OpenCart Templates

Page Objects with Selenium and Cucumber JVM

In this post I’ll focus on using page objects in a Selenium and Cucumber JVM context. Please note that this post follows on from the previous post, using the code you built up there.

If you did create everything from the last post, now would be a good time to rerun it and make sure it all works:

mvn clean test

I’m going to revise the example code from that previous post to use page objects. There are a lot of ways people structure all of this in Java. I’ll give you one approach here. If you followed along from the last post you definitely created a directory path of src/test/java. You may or may not have created a package structure within there. With my example, I had created a full path of src/test/java/com/testerstories.

Within that directory — or the one you created if you used a different package path — create a new directory called pages. This is where we’ll store our page objects. What we’re going to do is take the step definitions from the previous post and change them to use the page objects that we create. Let’s first consider what our step definition file ( looks like based on that previous work:

If we consider that code, we’re using two pages as part of the logic. Or, rather, we’re using parts of pages. In this case, I’m using a login form and checking for a navigation list. Neither of these are an entire page but rather sections or areas within a page. This makes for a good point right off the top: a page object does not necessarily have to represent an entire page. I’m not going to cover the entire theory around page objects in this post. There’s plenty of material out there.

So first let’s create a class in the pages directory you just created. Put the following code in it:

Now create in that same location and put the following in it:

I’m starting off kind of simple here just to show that page objects are nothing more than Java classes.

There a lot of different thoughts about how to construct page objects and how they should work. I’ll take the simple approach: I want to add methods on my page objects that essentially mimic actions that occur on those pages. So we’re going to start moving logic out of our LoginSteps and into our page objects.

If you go through the code, you’ll notice that the first thing I do in the LoginSteps is create an instance of the driver. This is done in a @Before method. Now, I could do that same thing in each page object so that when a page object is used, a browser is created. But, if I did that, it would mean that any time that page object is called, a new WebDriver instance is created. That doesn’t seem like a good idea. However, it is true that each page object is going to need an instance of the WebDriver so that it can run commands against it. But if that’s the case, which page do I put it in?

You don’t put it in one specific page. A good practice is that you create what some people call an abstract page or a base page. So in that pages directory, create a file called Put the following in it:

The first thing to do here is create an instance of WebDriver. But let’s keep something in mind: each of my page objects is going to inherit from this base page. That means I want each page instance to have access to the WebDriver instance. So let’s add this:

Here “protected” means any page inheriting from this page gets access to the driver variable. Now let’s create a constructor. We’re going to pass in the driver.

This simply sets up the driver variable so that it is associated with whatever driver is passed in. Note that the driver that is passed in must be of type WebDriver.

Now, here’s where approaches differ. One thing I want this base page to be able to do is navigate to the location of specific pages. At the very least, I need it to navigate to the login page. So let’s add the following:

Before getting into that, let’s go back to our @Given step in and change it like this:

Notice the lines that I commented out are the ones I moved to the base page. I’m commenting these out rather than removing them so you can see this evolve. This is going to render our test useless, of course, but what we’re going to do is rebuild the test using the page object pattern. When done, we should be able to rerun the test and see it execute as it did before.

Now let’s consider line 17 in What is that line actually doing? The contract of the page object model is to give you the ability to chain actions via the use of the page objects. How do I do that? Notice the navigateTo() method is set up to return a LoginPage object — i.e., an instance of the LoginPage class. But I need an instance of that page returned. That’s what line 17 is doing. Notice also that the driver instance is being passed to the newly created page object.

Now let’s go back to and change it as such:

Here I’ve extended the base page and added a constructor. What this is saying if I create a LoginPage and pass in a driver, I would call (via the super method) the constructor of the BasePage, since I’m extending that class via this one.

Now looking at our next part of the test — the @When step — I have to log in to the site as an admin. What I want to do now is move the logic from that step into the page object. So I want to translate the act of logging in to a method on the page object. So let’s change to look like this:

Notice how, once again, I return an instance of the LoginPage. Let’s once again change for the @When step as follows:

Again, all I’ve done is comment out the steps to show that those actions have now been moved to the page object.

The last test step, the @Then step, takes place after the login has occurred and checks for a navigation element that should be present. So let’s first update our similar to how we updated the

This is just extending the BasePage class and putting in the constructor as I originally did with LoginPage. Now I want to take the action from the @Then step and create a method for that action. So let’s update the so it looks like this:

And, as before, let’s comment out what we no longer need in the @Then step in

So now we have a test that does nothing! Well, it does open a browser and then close it — due to the @Before and @After hooks — but there is no test logic. Everything has been moved to the page objects. But the page objects aren’t being referenced by the test steps. Let’s rectify that. Here is the full, with all no-longer-needed material — including imports — removed. Further, the test steps have been updated to use the page object:

On lines 17 to 19 I’m simply declaring the page variables so that I can use them in all of the steps. Then in each step I create (if necessary) a page object instances and references methods on that instance. As you can see, the test logic is quite a bit more concise because it is delegating everything to the page objects.

Try to run the test again and make sure it works:

mvn clean test

Some people, like myself, don’t like to distribute how elements are referenced throughout the page object logic. So you can modify to look like this:

Spend a little time looking at the differences with this approach. I personally prefer something like this because it allows me to keep how the selectors for elements are defined isolated to one spot. I’ll take a moment here to admit that I’m a dynamic language person at heart and no matter how much cleanup you do, Java still provide way too much boilerplate for my liking. By contrast, consider a page definition in my Ruby-based Symbiont framework. The differences are like night and day.

Going back to the above approach, I should note that some folks like to use a PageFactorywhich can further clean up a page object, depending on your preferences. I’ll leave that as an exercise for the reader since it’s really just a slight variation on the theme.

The main goal for me here was to showcase a working example of the page object pattern but also to make sure you were aware that there are different thoughts on how to implement this pattern.

Even more importantly you see how to do this in a Java context, using Selenium and Cucumber JVM. This is an ecosystem that I’ve found some testers have a harder time getting involved, particularly if they have come from dynamic languages like Ruby or Python. Hopefully these few posts help you get started on using some of the more popular tools within the Java context for testing.

Autor do Post: JEFF NYMAN

Sobre Luiz Lohn

Luiz Lohn trabalha como QA Engineer em uma multinacional, além de realizar palestras e consultorias em empresas. Atua como um dos coordenadores do GUTS-SC (Grupo de usuário de teste de software), membro ativo na comunidade de qualidade e teste de software, ministra palestras e cursos. Especializando-se em segurança web e mobile.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.

Page Reader Press Enter to Read Page Content Out Loud Press Enter to Pause or Restart Reading Page Content Out Loud Press Enter to Stop Reading Page Content Out Loud Screen Reader Support