Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
UI Testing with Puppeteer

You're reading from   UI Testing with Puppeteer Implement end-to-end testing and browser automation using JavaScript and Node.js

Arrow left icon
Product type Paperback
Published in Mar 2021
Publisher Packt
ISBN-13 9781800206786
Length 316 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Dario Kondratiuk Dario Kondratiuk
Author Profile Icon Dario Kondratiuk
Dario Kondratiuk
Arrow right icon
View More author details
Toc

Table of Contents (12) Chapters Close

Preface 1. Chapter 1: Getting started with Puppeteer 2. Chapter 2: Automated Testing and Test runners FREE CHAPTER 3. Chapter 3: Navigating through a website 4. Chapter 4: Interacting with a page 5. Chapter 5: Waiting for elements and network calls 6. Chapter 6: Executing and Injecting JavaScript 7. Chapter 7: Generating Content with Puppeteer 8. Chapter 8: Environments emulation 9. Chapter 9: Scraping tools 10. Chapter 10: Evaluating and Improving the Performance of a Website 11. Other Books You May Enjoy

Organizing our code

Our first test was quite simple: we were just checking the page title. But let's take a look at the home page:

Packtpub home page

Packtpub home page

There are many actions we would like to test there:

  • Search for an existing book.
  • Search for a non-existing book.
  • Check the cart when it is empty.
  • Check the cart when we add a product.

Let's take, for example, Search tests. We would be doing the same steps every time:

  1. Click on the search box.
  2. Enter the text.
  3. Click on the search button.

We would be doing the same thing over and over in all our search tests. Sometimes there is a misconception that, as the test code is not production code, the code can be a mess. So, people go and copy/paste their tests over and over, duplicating code and hardcoding values. That ends up with hard-to-maintain tests. When tests are hard to maintain, they tend to be pushed down the priority list. Developers lose, QA analysts lose, and in the end, clients lose.

We are going to see two techniques to improve our test code: the Page Object Model (POM) and the test data config.

Introducing the Page Object Model

The POM is a design pattern that will help us separate our test code from the implementation of the interaction our tests will perform.

Let's build our HomePageModel together. What are the possible interactions on that page?

  • Go (to the page)
  • Get page title
  • Search
  • Sign In
  • View Cart
  • Go to Checkout
  • Subscribe

Well done! We just created our first Page Model. This is how it will look:

module.exports = class HomePageModel {
    go() {}
    title() {}
    search(searchValue) {}
    signIn() {}
    viewCart(){}
    gotoCheckout(){}
    subscribe(){}
}

Let's focus on the two first functions: the go function, which will navigate to the home page, and the title function, which will return the page title.

We will reuse a lot of code here. If we want to start using this model, we would need to do two things: implement the title fetching here and pass a Puppeteer page to this model:

export default class HomePageModel {
    constructor(page) {
        this.page = page;
    }
    // Unused functions…
    async go() {
        await this.page.goto('https://www.packtpub.com/');
    }
    async title() {
        return await this.page.title();
    }
}

Now it's a matter of importing this class into our tests using require. I will put this class into a POM (Page Object Model) folder inside the test folder. Once we create the file, we import it:

const HomePageModel = require('./pom/HomePageModel.js');

We declare a variable inside the describe:

let homePageModel;

We create an instance of this class in the beforeEach hook:

beforeEach(async () => {
    page = await browser.newPage();
    homePageModel = new HomePageModel(page);
    await homePageModel.go();
});

And now, we simply replace the page.title we are using with homePageModel.title:

(await homePageModel.title()).should.contain('Packt');

As I mentioned earlier in the chapter, UI tests help us see whether our refactoring broke our code. Let's run npm test again to confirm that we didn't break anything:

Test result after the first refactor

Test result after the first refactor

There's only one thing left to do so that we can be proud of our first project. We need to get rid of our hardcoded values. We only wrote two tests, and we have three hardcoded values: the site URL and the Packt and the Books words.

For these tests, we can leave these hardcoded values. But what if you have different environments? You would need to make the URL dynamic. What if your site were a generic e-commerce site? The brand name would depend on the test you are navigating.

There are many other use cases:

  • Test users and passwords
  • Product to test
  • Keywords to use

We can create a config.js file with all the environment settings and return only the one we get on an environment variable. If not set, we return the local version:

module.exports = ({
    local: {
        baseURL: 'https://www.packtpub.com/',
        brandName: 'Packt',
        mainProductName: 'Books'
    },
    test: {},
    prod: {},
})[process.env.TESTENV || 'local']

If this looks a little bit scary, don't worry, it's not that complex:

  • It returns an object with three properties: local, test, and prod.
  • In JavaScript, you can access a property by using object.property or by treating the object as a dictionary: object['local'].
  • process.env allows us to read environment variables. We won't be using environment variables in this book, but I wanted to show you the final solution.
  • Finally, we are going to return only the local, test, or prod property based on the TESTENV variable or 'local' if the environment variable was not set.

I bet that by now, you will know that we will be able to access this object using a require call:

const config = require('./config');

And from there, start using the config variable instead of hardcoded values. We would also need to pass this config to the page model because we have a hardcoded URL there.

After making all these changes, this is what our tests should look like:

const puppeteer = require('puppeteer');
const expect = require('chai').expect;
const should = require('chai').should();
const HomePageModel = require('./pom/HomePageModel.js');
const config = require('./config');
describe('Home page header', () => {
    let browser;
    let page;
    let homePageModel;
    before(async () => browser = await puppeteer.launch());
    beforeEach(async () => {
        page = await browser.newPage();
        homePageModel = new HomePageModel(page, config);
        await homePageModel.go();
    });
    afterEach(() => page.close());
    after(() => browser.close());
    it('Title should have Packt name', async() => {
        (await homePageModel.title()).should.contain(config.brandName);
    });
    it('Title should mention Books', async() => {
        expect(await homePageModel.title()).to.contain(config.mainProductName);
    });
});

If we remove all the unused functions, our final page model would look like this:

module.exports = class HomePageModel {
    constructor(page, config) {
        this.page = page;
        this.config = config;
    }
    async go() {
        await this.page.goto(this.config.baseURL);
    }
    async title() {
        return await this.page.title();
    }
}

As you can see, we didn't need to implement complex design patterns to make our tests reusable and easy to maintain. I think it's time to get started with our tests, which we will do in Chapter 3, Navigating through a website.

You have been reading a chapter from
UI Testing with Puppeteer
Published in: Mar 2021
Publisher: Packt
ISBN-13: 9781800206786
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime