Book Image

Modern Web Testing with TestCafe

By : Dmytro Shpakovskyi
Book Image

Modern Web Testing with TestCafe

By: Dmytro Shpakovskyi

Overview of this book

TestCafe is an open source end-to-end testing framework that combines unmatched ease of use with advanced automation and robust built-in stability mechanisms. This book is a comprehensive, project-based introduction to TestCafe that will show you how to use the TestCafe framework and enable you to write fast and reliable tests; plus you’ll have a proof of concept ready to demonstrate the practical potential of TestCafe. You’ll begin by learning how to write end-to-end web tests using the TestCafe syntax and features of the TestCafe framework. You’ll then go from setting up the environment all the way through to writing production-ready tests. You’ll also find out how to build a sample set of tests in a step-by-step manner and use TestCafe to log in to the website, verify the elements present on different pages, create/delete entities, and run custom JavaScript code. As you advance, you’ll delve into several stages of refactoring that will take you through the showcase setup/teardown and PageObject patterns. While this test suite is relatively simple to build, it showcases some of the most prominent features of TestCafe. Finally, this TestCafe book will show you how the tests can be run on a free and simple-to-use website, without requiring you to build and deploy your own servers or backend services. By the end of this book, you’ll have learned how to write and enhance end-to-end tests with TestCafe to solve real-world problems and deliver results.
Table of Contents (9 chapters)

Learning about the TestCafe API

Since the server-side code runs in Node.js, tests should be written in JavaScript (TypeScript and CoffeeScript are also supported, but eventually, everything should be transpiled into JavaScript).

TestCafe utilizes a minimalistic API that provides less than a few dozen methods, which are then transformed into user actions on the page. As our tests will be using the TestCafe API methods to interact with the pages, let's review the main interaction groups supported in TestCafe:

  • Elements selection.
  • Actions.
  • Assertions.
  • User roles.

Let's discover each of these interactions in more detail.

Elements selection

TestCafe utilizes an advanced mechanism with built-in waiting to locate target elements for an action or assertion. To perform an action (such as click, hover, type, and so on) or to make an assertion, you should first identify the target page element. This is as easy as specifying a standard CSS selector. For more complex situations, you can chain methods (such as, for example, getting an element by class name, then getting its second child, and then finally, getting its third sibling). Selector strings should be passed inside chainable Selector constructors to create a selector.

For example, you can click on a button with the button-test class, as follows:

const { Selector } = require('testcafe');const buttonTest = Selector('.button-test');

For more complex situations, you can traverse the DOM tree by chaining selectors:

const { Selector } = require('testcafe');const linkTest = Selector('#block-test')    .child('a')    .withAttribute('href', 'https://test-site.com/main.html')    .withText('Second link');

What this chain of selectors does is the following:

  1. Selects an element with the block-test id.
  2. Selects its child elements.
  3. Filters them by the a tag.
  4. Selects elements with the href attribute that includes https://test-site.com/main.html.
  5. Selects elements that include the Second link text.

    Note

    If a selector matches several elements, the subsequent methods return results for all the elements that were matched.

TestCafe provides a number of methods that search for elements relative to the selected element (keep in mind that all of these methods should be prepended with Selector(cssSelector)). Most of these methods accept index as an argument, which should be a zero-based number (0 will be the closest relative element in the set). If the number is negative, the index is counted from the end of the matched set. Here are the methods:

Now, let's look at the methods that filter elements from the selector. The same as before, all of these methods should be prepended with Selector(cssSelector). Here are the methods:

When a selector is executed, TestCafe will be waiting for the target node to appear on the page until the selector timeout expires. You can specify the timeout (in milliseconds) in the following cases:

During the timeout, the selector is rerun until it returns a DOM element or the timeout is surpassed. If TestCafe can't find the corresponding node in the DOM, the test is marked as failed.

Actions

The TestCafe API provides a set of action methods to interact with the page (such as click, type, select text, hover, and so on). You can call them one after another in a chained fashion. All of these methods should be prepended with t as they are the methods of the test controller object (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/). Also, selector can be a string, selector, DOM node, function, or Promise; and optionally, you can use options, which is an object with a set of options containing supplementary parameters for the action (unless otherwise specified). Here are all the main action methods:

Assertions

TestCafe allows you to verify elements, page properties, and parameters (equals, contains, greater, match, and so on). To write assertions, use the test controller's t.expect method, followed by an assertion method that accepts an expected value and optional arguments; message is the assertion message string that shows up in the report if the test fails and options is an object with a set of options containing supplementary parameters for the assertion. Here are all the assertion methods available in TestCafe out of the box:

User roles

TestCafe has a built-in user role mechanism that emulates user actions for logging in to a website. It also saves the logged-in state of each user in a separate role that can be reused later on in any part of your tests to switch between user accounts. This approach gives access to some unique features:

  • Login actions are not duplicated upon switching to a previously used role during the same session. So, for example, if you activate a role in the beforeEach hook, the login actions will run only once before the first test. All further tests will just reuse the existing authentication data.
  • When you switch roles, the browser automatically navigates back to the page where the switch happened, so there is no need to additionally open any URLs for a new role (this behavior can be disabled if required).
  • If during a test you log in to several websites, authentication data from cookies and browser storage is saved in the active role. When switching back to this role in the same test, you will be logged in to all the websites automatically.
  • An anonymous built-in role that logs you out of all accounts.

Let's have a look at a practical example of creating and using roles.

To create and initialize a role, we will need to use a Role constructor. Then, the login page URL and actions needed to log in should be passed to Role. This is shown in the following code block:

const { Role, Selector } = require('testcafe');const regularUser = Role('https://test-site.com/login', async (t) => {    await t.typeText('.login', 'TestUser')        .typeText('.password', 'testuserpass')        .click('#log-in');});const admin = Role('https://test-site.com/login', async (t) => {    await t.typeText('.login', 'TestAdmin')        .typeText('.password', 'testadminpass')        .click('#log-in');});const linkLoggedInUser = Selector('.link-logged-in-user');const linkLoggedInAdmin = Selector('.link-logged-in-admin');fixture('My first test Fixture').page('https://test-site.com');test('Test login with three users', async (t) => {    await t.useRole(regularUser)        .expect(linkLoggedInUser.exists).ok()        .useRole(admin)        .expect(linkLoggedInUser.exists).notOk()        .expect(linkLoggedInAdmin.exists).ok()        .useRole(Role.anonymous())        .expect(linkLoggedInUser.exists).notOk()        .expect(linkLoggedInAdmin.exists).notOk();});

After you create all the required roles, you can switch between them anytime; roles are shared across tests and fixtures. Roles can even be created in a separate file and then used in any test fixture that references (requires or imports) this file.

To sum up, in this section, we reviewed the TestCafe API and the main methods that it provides. We also learned how to select elements, conduct assertions, and utilize user roles to switch between different accounts. Now, let's take a look at how custom client-side code can be executed in TestCafe to give us even more control over the browser.