WARNING
This will be a looong post. So, grab a cup of coffee/tea and hang on tight!
Introduction
What is E2E?
End-To-End (E2E) testing is a technique used to test an entire flow as if we were an actual user by simulating their actions (clicks, pressing certain keys, typing into a field, etc).
This is quite useful for testers, since as the project grows the amount of scenarios to test also increases and having humans do all that work can result in them overlooking/missing things because of distractions or fatigue.
Also, these tests can be configured to run on a Continuous Integration
(CI) system that can execute all the tests every X amount of time or after a deploy. But that's a topic for another article :)
What is TestCafe?
TestCafe is a tool for E2E testing based on NodeJS that is fairly easy to setup and use that supports both JavaScript and TypeScript.
Requirements
Install NodeJS
This can be done via their website or via your operating system's terminal/command line (steps will be different depending on your OS).
After that, in order to verify that your NodeJS was installed successfully, run the following command in your terminal/command line:
npm -v
You should be able to see the version number, mine is 5.6.0
as I write this post.
Install TestCafe
Now that we have npm
installed, run the following command (This may require root/admin privileges):
npm install -g testcafe
To run this as a root/admin user, execute the same command in macOS or a linux based OS while also adding sudo
at the beginning of the command above, and Windows users should right click
and select Run as administrator
when opening the cmd
or PowerShell
.
If everything went well, you should be able to see the version of your TestCafe module by running:
testcafe -v
The output should say something like:
Using locally installed version of TestCafe.
0.18.6
Great job! We are almost ready to start 👨💻 /👩💻.
Choose your weapon (IDE/editor)
I'll be using VS Code as my weapon of choice + some extensions (I'll show them later, I'm also excited to start coding!) but feel free to choose whatever IDE/editor you prefer.
Project Structure
Our initial project structure will look like this:
project
|
└─── tests
│
└─── devto.js
First test
The first thing we need to do is to import
a class called Selector
from the testcafe
module, like this:
import { Selector } from 'testcafe'
Then, we need to create a fixture
, give it a name and the page
url that will be used at the beginning of all your tests.
You will see where the name of the fixture
is used later.
Now your code should look like:
import { Selector } from 'testcafe'
fixture('DEV Tests')
.page('http://dev.to/');
Now that we have this, let's start writing the code for our tests.
For simplicity, these will be the steps we will perform:
- Go to dev.to home page.
- Click on the About link.
- Check every founder's name.
Back to the code-cave!
In order to do this we need to add a test
method which takes two arguments: A String
value which will be the name of the test and an async
arrow function which will have the logic inside.
Something like:
test("Check founder's names", async (t) => {
// Brace yourself, the real magic goes here.
});
Why do we use async
?
This will allow us to run our tests in parallel (which is awesome!) later on. But for now, we will run our tests in a single browser.
Where is the real magic at?
Why did we import
the Selector
class?
What does the fox say?
Ssssh... just let it happen.
We will get there :)
First thing we need to do is to get the about link's selector
.
A
selector
is basically a way to identify a element or group of elements on a page.
To acheive this, I'll use Chrome's built-in DevTools. Head over to the dev.to home page, scroll down and right click
on top of the About link and select the Inspect
option.
This will open up the DevTools options and you want to focus on the <a>
HTML element highlighted.
On the DevTools panel, right click
on the <a>
of the About link and select Copy > copy selector
. This will copy the selector
value to your clipboard.
Now, go back to your editor and store that value like this:
const aboutLink = Selector('#sidebar-wrapper-left > div.side-bar > div.widget > div.side-footer > a:nth-child(1)');
All the code should look like this now:
import { Selector } from 'testcafe'
fixture('DEV Tests')
.page('http://dev.to/');
test("Check founder's names", async (t) => {
const aboutLink = Selector('#sidebar-wrapper-left > div.side-bar > div.widget > div.side-footer > a:nth-child(1)');
});
But wait!
Let's take this as a chance to use another spell that TestCafe provides us with... the .withText()
method.
We can write the same code like this:
const aboutLink = Selector('a').withText('About');
The .withText()
method takes a String
argument
and works similarly to the .contains()
method you may be familiar with. It will compare the element's text value with the argument
you pass in and only return true
when the argument
matches with any part of the given text value (note: it is case-sensitive); otherwise, it will return false
.
Case-sensitive means that the capitalization of a letter makes it different from another, even if they sound the same. Example: "test" and "Test" are considered to be different in this case.
Let's head over to the About page to find the other selector
values we will need to use.
We could do it the copy-pasta way, and repeat all the steps we did with the DevTools, but we know we are better than that (or so we think).
So, let's do it like we just learned. With the power of the withText()
method.
The only difference is that this time our selector
is not a <a>
tag, it's actually a <b>
tag. You can verify it by inspecting each of the founder's names or just trusting my word (I actually wouldn't).
Our code would look something like this now:
const aboutLink = Selector('a').withText('About');
const firstFounder = Selector('b').withText('Ben Halpern');
const secondFounder = Selector('b').withText('Jesse Lee');
const thirdFounder = Selector('b').withText('Peter Frank');
Awesome!
If we look at the entire devto.js
file, it should be:
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('Jesse Lee');
const thirdFounder = Selector('b').withText('Peter Frank');
});
Now let's start using our new shiny selectors!
Did you forget what we were actually gonna do in our test?
Yeah, me too. But don't worry, I gotchu fam! 😎
- Go to dev.to home page.
- Click on the About link.
- Check every founder's name.
The .page()
method already covers the first step, so we can mark that one.
[x] Go to dev.to home page.
[ ] Click on the About link.
[ ] Check every founder's name.
In order to click on the "About Link" we will need to add the following code at the end of our test:
await t
.click(aboutLink);
After that, we will have to check if every founder name header is displayed on the page:
await t
.click(aboutLink)
.expect(firstFounder.exists).ok()
.expect(secondFounder.exists).ok()
.expect(thirdFounder.exists).ok();
Let's take a step back and verify that everything looks like this in our devto.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();
});
Are you still there?
Well, I hope you do. Because now comes the fun part!
Run the test
In order to run the test you will need to make your way to the folder where your devto.js
is located and run the following command:
testcafe chrome devto.js
Note: you can replace
chrome
with your favorite browser.
Now aliens will take control over your computer and start doing crazy stuff... like running your test.
If everything went well, you should see something like this in your console:
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
1 passed (4s)
Woof!
That was quite a lot of work!
But there are still more goodies to learn.
- Using multiple browsers at once.
- Running tests in parallel.
- Refactor our code to use Page Object Model design pattern.