E2E Testing with TestCafe | Refactoring

Christian Vasquez - Jan 16 '18 - - Dev Community

On Part 3 we learned how to run not only multiple browsers in parallel, but also multiple instances of those same browsers.

So what can we do to improve our TestCafe project?

If you followed this guide since we started, your devto.js should look like this:

import { Selector } from 'testcafe'

fixture('DEV Tests')
    .page('http://dev.to/');

test("Check founder's names", async(t) => {
    const aboutLink = Selector('a').withText('About');
    const firstFounder = Selector('b').withText('Ben Halpern');
    const secondFounder = Selector('b').withText('Jess Lee');
    const thirdFounder = Selector('b').withText('Peter Frank');

    await t
    .click(aboutLink)
    .expect(firstFounder.exists).ok()
    .expect(secondFounder.exists).ok()
    .expect(thirdFounder.exists).ok();
});

test("Search for posts about testing", async (t) => {
    const discussTag = Selector('span').withText('#discuss');
    const discussTitle = Selector('h1').withText('Discussion');

    await t
        .click(discussTag)
        .expect(discussTitle.exists).ok();
});
Enter fullscreen mode Exit fullscreen mode

What can we possibly refactor?

It works just fine, right?

Correct, it does works. But you know what? We can take it to the next level. So let's go!

Refactor

The first thing we will need is to find a better way to use our Selectors. Not by using a different method or syntax, but actually abstracting them from our test functions.

Our tests know too much right now.

Smartass

So, how can we do that?

Introducing... Page Object Model

This design pattern consists of making classes that represent a given page, based on the functionality they are used for.

For example

DevSearchPage.js

This page should hold the Selectors that are needed to make a search. That means that any filter and articles that may show up, should be part of it.

This makes our code go from being:

const aboutLink = Selector('a').withText('About');
Enter fullscreen mode Exit fullscreen mode

To:

import DevHomePage from '../page/devhomepage.js';

const devHomePage = new DevHomePage();

// ...

await t
    .click(devHomePage.aboutLink)
    //...
Enter fullscreen mode Exit fullscreen mode

Now there's no doubt about which "About Link" we are using in our tests, while also making our Selector be available for other tests that might need it, also known as reusability.

So, our final result should look like this:

// project/page/devhomepage.js

import { Selector } from 'testcafe';

export default class DevHomePage {

    constructor() {
        this.aboutLink = Selector('a').withText('About');
        this.discussTag = Selector('span').withText('#discuss');
    }

}
Enter fullscreen mode Exit fullscreen mode
// project/page/devaboutpage.js

import { Selector } from 'testcafe';

export default class DevAboutPage {

    constructor() {
        this.firstFounder = Selector('b').withText('Ben Halpern');
        this.secondFounder = Selector('b').withText('Jess Lee');
        this.thirdFounder = Selector('b').withText('Peter Frank');
    }

}
Enter fullscreen mode Exit fullscreen mode
//project/page/devdiscusstagpage.js

import { Selector } from 'testcafe';

export default class DevDiscussTagPage {

    constructor() {
        this.discussTitle = Selector('h1').withText('Discussion');
    }

}
Enter fullscreen mode Exit fullscreen mode

And now we can use all of our Pages in our tests like this:

//project/tests/devto.js

import DevHomePage from "../page/devhomepage";
import DevAboutPage from "../page/devaboutpage";
import DevDiscussTagPage from "../page/devdiscusstagpage";

const devHomePage = new DevHomePage();
const devAboutPage = new DevAboutPage();
const devDiscussTagPage = new DevDiscussTagPage();

fixture('DEV Tests')
    .page('http://dev.to/');

test("Check founder's names", async(t) => {
    await t
        .click(devHomePage.aboutLink)
        .expect(devAboutPage.firstFounder.exists).ok()
        .expect(devAboutPage.secondFounder.exists).ok()
        .expect(devAboutPage.thirdFounder.exists).ok();
});

test("Filter articles by discuss tag", async (t) => {
    await t
        .click(devHomePage.discussTag)
        .expect(devDiscussTagPage.discussTitle.exists).ok();
});
Enter fullscreen mode Exit fullscreen mode

But as always, never trust random post on the internet and try it out for yourself!

If you see:

Using locally installed version of TestCafe.
 Running tests in:
 - Chrome 63.0.3239 / Mac OS X 10.13.2

 DEV Tests
 ✓ Check founder's names
 ✓ Search for posts about testing


 2 passed (6s)
Enter fullscreen mode Exit fullscreen mode

It means my job here is done 😎

You can probably continue your journey without me babysitting you.

So go eat the official documentation and

Learn all the things

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .