Let’s Build A Framework – Day 2.5


Despite my best efforts we fell a little behind in the last 2 sessions.  So to catch us up to where I wanted to be on our final day we mae this Day 2.5 session post.  This post will cover a few key changes made after Day 2.   We will discuss these on Day 3 as well, but this post just covers the actual coding done.

I will keep this post as short and as sweet as possible.

What Has Changed Since Day 2

If you’ll remember on Day 2, we built a handy dandy function “waitForPageToLoad” that allows us to quickly check all required elements on a page and ensure that page is fully loaded.   However, the way we built it, was to add it directly to our EbayHome page object.  This means that only the EbayHome page could use it.  When what we want is for any page we create to be able to use waitForPageToLoad.

To do this we are going to create a Base Page Object and explore the concept of inheritance in javascript.  This will also let us clean up our code a little bit in other ways.

The other changes made were to create a couple new pages for us to play with, as up until now we’ve been doing everything on a single EbayHome page.  By the end of this post you should have 3 different pages and we can play with navigating between all of them and really start to see the power behind our framework.

Building a Base Page Object in WebDriver.io

As mentioned, our big goal for this session is to share methods between our page objects.  Now, there are plenty of options we could explore,  but, since we are already using classes and objects in our framework we should really take advantage of the features for them.

The biggest feature we’ll use is “inheritance”.  That is to say, allowing one class to be built on top of another class.  This means that the child class  would get all of the benefits and methods of the parent class while also being able to have its own methods.

Note: In computer science inheritance can be a controversial subject on when it should and should not be used.  If you are new to programming and want to learn more I recommend googling concepts such as “inheritance vs. composition”.   But for the purpose of our sessions we are going to stay away from these arguments and use inheritance in a very limited capacity.

Let’s Get To It

Our goal is to build is a new page object base class we’ll call “PageObject”.  This class will have a constructor just like our other page that saves our driver.  We will also use a little trick to have it load our locator file dynamically to save us some coding time.

But, most importantly we are going to put our “waitForPageToLoad” method there.  Doing this will allow any page object like EbayHome that inherits from PageObject to be able to call “waitForPageToLoad” with no extra code needed.

Building Our Base PageObject class

First step is we are going to make a very simple class, and only have it handle the driver.  This is going to look very similar to our existing EbayHome class.

Screen Shot 2016-12-08 at 12.54.03 PM.png
A very basic PageObject base class

So at this point with our EbayHome class inheriting our base class, all we’ve gained is it no longer has to manage its own driver.

Screen Shot 2016-12-08 at 12.55.58 PM.png
EbayHome inheriting our PageObject base class

First thing, let’s get rid of the need to constantly have to load the locator file in each page.   This is problematic because if we ever move our page object file, or change the locator name our tests will stop working.  Instead, we want our base class to be smart enough to say “Look for a locator file that has the same name as this class and load that”.

Luckily we can get the name of our EbayHome class even from inside our base class with

this.constructor.name

Calling that in our PageObject base class will return “EbayHome”.  So we can modify our PageObject constructor to automatically load our locator file for every page that inherits from it.

Screen Shot 2016-12-08 at 12.59.33 PM.png
Dynamically load the correct locator file based on the page objects name (EbayHome etc …)

Obviously now, we can delete the locator line from our EbayHome constructor.

Next up, we want to migrate our “waitForPageToLoad” method to our PageObject base class so for any new page object we create we can call this method.  This is fortunately very easy as we’ve already written the function.  We can just copy the waitForPageToLoad method directly to our PageObject class.

Screen Shot 2016-12-08 at 1.02.10 PM.png
waitForPageToLoad now in the base class

Note: the one thing we have to change is, its now a prototype of the PageObject class and not from the EbayHome class.

Now obviously this method relies on our helper method “findRequiredLocators” so we’ll have to move that over to our PageObject class as well.

Screen Shot 2016-12-08 at 1.03.59 PM.png
findRequiredLocators method now in PageObject class

Inheriting From Our PageObject Class

There are numerous ways to inherit in Node/JavaScript, in newer versions like ES6 its much more user friendly.  But because we want this post to work for everyone including people who might be on older infrastructure, we are going to use an older method of inheritance

I leave it to you the reader to make a modification yourself and use the new fancy methods of inheritance for future learning and practice.

For our method of inheritance we are going to use a helper method in the ‘util’ library called “inherits”.

util.inherits(EbayHome, PageObject);

That line will do most of the dirty work for us in linking EbayHome as a child of PageObject.

The only remaining thing is to use PageObject’s constructor.  So when we create a new page object in our tests we can pass a driver in to the page, but have that page pass it up the chain to its parents constructor.

Thankfully there is an easy solution for this.  We just call that driver directly from our parent class like so:

function EbayHome(passedInDriver) {
 PageObject.call(this, passedInDriver);
 locators = require('../locators/EbayHome');
};

Note that this is our EbayHome class, and inside we are referencing our new PageObject class and calling its constructor.

After these two changes are made, we have successfully made EbayHome a child of our PageObject base class in javascript.

screen-shot-2016-12-08-at-1-46-29-pm

And that’s it, nothing else needs to change.  You can still call waitForPageToLoad in your tests like before only now we’ve set it up so it will work for every new page we create as well.

on.ebayHome().waitForPageToLoad()

Speaking of new pages ..

Expanding our Page Object Collection

Now that we have a few commands, a PageObject base class and some helpful methods.  It’s probably time to create some new page objects so we can make bigger scripts.

For this session we are going to create a EbaySearchResults and EbayCart page, and make a test to tie it all together.

Steps To Create a New Page

Obviously in Day 1 and 2 we covered making a new page.  But now is a good time for a refresher.

  1.  Create a new PageObject class under the pages directory

    1. Make sure that class inherits from our PageObject base class
    2. We’ve created a “SamplePageObject” class for you.  If you are in a hurry you can just copy this class for new pages and change “SamplePageObject” to your new page name
Screen Shot 2016-12-08 at 1.10.17 PM.png
A sample new page object template

2 Create a new locator file with the same name as the page under the locators directory

  1. Just like with the page object, we’ve created a SamplePageObject.json file you can use as a template
Screen Shot 2016-12-08 at 1.12.09 PM.png
Sample page object locator file

3 Add an entry to our PageObjectCollection class

  1. Now that we have a page, and a locator file, the final step to wiring it up is to add an entry to our PageObjectCollection class which is located in the pages/page_object_collection.js file.
Screen Shot 2016-12-08 at 1.15.34 PM.png
New entry for a page object in the PageObjectCollection class

4.  Fill out the new page with locators and methods

  1. Now you can add locators to the locator file for elements that are on the page.  And you can add methods in the page to interact with those elements.
  2. Below are screenshots of new locators and methods for our EbaySearchResults, and EbayCart pages!
Screen Shot 2016-12-08 at 1.17.27 PM.png
Locators for new EbaySearchResult page
Screen Shot 2016-12-08 at 1.18.20 PM.png
Locator for new EbayCart page
Screen Shot 2016-12-08 at 1.19.13 PM.png
New EbaySearchResult page with a goToCart method
Screen Shot 2016-12-08 at 1.20.01 PM.png
New EbayCart page

Putting It All Together With a New Test

Now that we have our shiny new PageObject base class, and some new page objects.  Let’s write another test, this time navigating further into the site.

Screen Shot 2016-12-08 at 1.22.00 PM.png
New test using new page objects

Note how we can call waitForPageToLoad on any page we want.

Hopefully now, you’ll start to see how this framework while kind of a pain to build adds some real value.

  • At this point our tests are readable even for people who can’t code well.
  • We can quickly spin up new pages in a matter of minutes and incorporate them into our tests.
  • We can add new methods to all our pages at once with our base class.

And, believe it or not, we have only scratched the surface with what is possible with our framework.

What’s Coming Up?

For our final day of work, we’ve still got some fun features to add.

  1. We’re going to create a concept of a “Navigation Path” that will allow us to quickly get to any page we want.
  2. We’re going to create a smoke test, that takes advantage of everything we have and that can test every single page we have in one simple data driven test.
  3. We’re going to create some API infrastructure and tests.

 

So stay tuned dear reader because we’ve saved the best for last.

 

Advertisements

One thought on “Let’s Build A Framework – Day 2.5

  1. Pingback: Testing Bits – 12/4/16 – 12/10/16 | Testing Curator Blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s