E2E Testing with TestCafe | Refactoring
Tags: testing, e2e, testcafe, javascript
Published: 2018-01-16 03:05:01

---
title: E2E Testing with TestCafe | Refactoring
published: true
tags: testing, e2e, testcafe, javascript
cover_image: https://res.cloudinary.com/practicaldev/image/fetch/s--FiNIdQbx--/c_imagga_scale,f_auto,fl_progressive,h_500,q_auto,w_1000/https://community.devexpress.com/blogs/thinking/TestCafe/browserstack-img-1.png
series: E2E testing with TestCafe
---

On [Part 3](https://dev.to/chrisvasqm/e2e-testing-with-testcafe--part-3-2io7) 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:

```js
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();
});
```

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 `Selector`s. Not by using a different method or syntax, but actually abstracting them from our `test` functions.

Our tests _know too much_ right now.

![Smartass](https://media.giphy.com/media/l0Iy4M0jO48jGB3R6/giphy.gif)

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 `Selector`s 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:

```js
const aboutLink = Selector('a').withText('About');
```

To:

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

const devHomePage = new DevHomePage();

// ...

await t
    .click(devHomePage.aboutLink)
    //...
```

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:

```js
// 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');
    }

}
```

```js
// 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');
    }

}
```

```js
//project/page/devdiscusstagpage.js

import { Selector } from 'testcafe';

export default class DevDiscussTagPage {

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

}
```

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

```js
//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();
});
```

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)
```

It means my job here is done 😎

You can probably continue your journey without me babysitting you. 

So go eat the [official documentation](http://devexpress.github.io/testcafe/documentation/test-api/) and 

![Learn all the things](https://thepracticaldev.s3.amazonaws.com/i/synotanhnck9ym5nypn1.jpg)