Lots of applications have a set of processes or operations that include sending emails to customers. This could be related to registration, resetting passwords, or notifications… Sometimes, teams choose to avoid the automation testing of email verifications and stick to testing it manually at the implementation time. This might be a fair choice if the area that is being tested isn’t going to be updated in the future, or isn’t impacting the business progress or success.
To automate or not to automate? Ask yourself the following couple of questions:
- Is this an important part of the business, would it cost us money if an email wasn’t sent properly, or sent with incorrect contents/links? — Most of the time, the answer is yes.
- Are we re-visiting this test frequently, more than one time every few months? — Sometimes we won’t revisit because it’s time-consuming or boring — this also means a yes.
Automating this test would enable us to validate that multiple systems that integrate together are working properly. For example, the front-end that is requesting the operation, the API/service that is performing the operation of sending an email, the email server setup, the proper links or contents submitted in the emails, etc.
If emails related services are developed/configured internally without integration with a third party, then it’s easier to use tools such as mailhog to perform the testing. Also, it’s faster as you won’t need to use the internet to check the receipt of an email. However, sometimes, we use external services for authentication & authorization (e.g. auth0). In this case, it would be better to use Gmail to test emails, as it might be tricky to expose your mailhog server to the public for it to receive an email from a third party service on the internet.
This post shows how to integrate with Gmail using typescript. This utility can be used when testing email verification.
Pre-requisites
- Install Node.js and npm. This site explains what are these and how to install them
- Install typescript
- Create a Gmail account for testing
Setup
- Create a new repository on your local environment and navigate to it:
mkdir gmail-helper && cd gmail-helper
- Inside the repository run:
npm init -y, this will create a package.json file
- Install ts-node:
npm i -D ts-nodeand @types/node:
npm i -D @types/node
- Install the client library:
npm i googleapis --save
- Navigate to, and enable the GMAIL API
* Click the “Download client configuration” button, this will download a credentials.json file, which we will use for authentication
* Copy the credentials.json file to the gmail-helper folder created earlier
- Copy the example from the quickstart page into a new index.js file in the gmail-helper folder
* In the index.js script, search for the SCOPES array, and change it from ’’ to ’’, which will give full access to the account — Be careful if you’re using this for anything other than testing
* when running this script for the first time it will generate a token.json file, containing the user’s access and refresh tokens; we will use these tokens to perform operations related to the gmail account, such as getting email messages, deleting messages, etc. This means we will only need the index.js file for this first single run only — Check more details in the table below
* Run the script:
node index.js
** Navigate to the link returned, and grant access to the application
** A new page opens showing an authorization code. Copy it to the terminal where you ran
node index.js
** Notice the token.json file created — The index.js will no longer be needed unless the tokens.js file was deleted, or the user revoked the access granted manually in Google Console.
Make sure not to upload your tokens and credentials to a public repository, as this provides access to anyone to fully control your account.
How does the authorization work?
If you are familiar with how the authorization works you could skip this section (this is an explanation of the steps we’ve done in the setup).
- Obtain OAuth2.0 client credentials from google console (the same one we got when we enabled the GMAIL API) — ClientId and ClientSecret
- Using these credentials, we request an access token from Google Authorization Server (also done in the setup section)
* Before our app can access private data using Google API, it must obtain an access token that grants access to that API.
* During this token request process, the application sends one or more values in the scope parameter (the one we updated in the SCOPES array in the setup section). This is mainly our application identifying the permissions it needs. In this step the user also consents to give grant permissions to the gmail account used.
* The Google authorization server sends an authorization code, which the application can exchange for an access token.
* Authorization information is stored on the file system, so subsequent executions will not prompt for authorization.
- Later when we need to interact with Google API, we rely on this token, which is stored in token.js file in our case
For more information, have a look at
Implementation
- Create a new file gmailHelper.ts
- Import the following 3 libraries: fs, path, and googleapis. fs allows us to interact with the file system, path enables us to get the path of the files we need, and googleapis enables us to interact with the Gmail API:
import * as fs from 'fs';
import * as path from 'path';
import { google } from 'googleapis';
- The following instructions get the path of credentials.json and tokens.json, and throw an error if they didn’t exist:
const credentialsPath = path.join(__dirname, 'credentials.json');
const tokenPath = path.join(__dirname, 'token.json');
if (!fs.existsSync(credentialsPath) || !fs.existsSync(tokenPath)) {
throw new Error('Could not find credentials.json or token.json in: ' + __dirname + '.
Please follow the steps at to generate them!');
}
- The following instructions create an OAuth2 client and authorize the user to access the Google APIs:
const credentials = fs.readFileSync(path.resolve(__dirname, credentialsPath), 'utf8');const { client_secret, client_id, redirect_uris } = JSON.parse(credentials).installed;const oauth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);const token = fs.readFileSync(path.resolve(__dirname, tokenPath), 'utf8');oauth2Client.setCredentials(JSON.parse(token));
// set the authentication client at the service-level
const gmail = google.gmail({
version: 'v1',
auth: oauth2Client,
timeout: 10000
})
Now we can start creating functions that enable us to interact with Gmail
- List the labels
export const listLabels = async () => {
try {
const res = await gmail.users.labels.list({
userId: 'me',
});
return res;
}
catch (e) {
throw new Error('Unable to retrieve the list of email messages: ' + e);
}
};
Let us try it: Add
console.log(res); before the return in the listLabel function. Add
listLabels(); at the end of the gmailHelper.ts file, and in your terminal run
ts-node gmailHelper.ts
- Get message content
export const getMessageContent = async (id: string) => {
try {
const messageContent = await gmail.users.messages.get({
id,
userId: 'me',
format: 'full'
});
return messageContent;
} catch (e) {
throw new Error('Unable to retrieve message content: ' + e);
}
};
You can use the result of this function to parse the content of the message, and open or verify the link that you need.
These functions can be called in the steps definitions. Check the full source code at:, it contains some additional functions that might be helpful.
Thanks for reading. Please let me know your feedback :)
This post is inspired by the following article written by Angie Jones:.