One of our clients who tasted success with test automation of legacy desktop application approached us with requirement to automate a web-based application. However, they have one condition that we use CodeceptJS as test automation tool.
Even though we were given a condition to automate with CodeceptJS, still we started to do a POC to check if we can automate using CodeceptJS.
To our surprise codeceptJS is a cool tool with following features
- Scenario Driven
- Easily readable code
- It can run test using WebDriver, Puppeteer, TestCafe, Protractor, Appium
- It can automate Web and Mobile applications.
- It has good integrations to Allure and Mochasome reports and lot of other cool stuff.
So, after learning some interesting stuff about CodeceptJS we continued with our POC.
Some of the checkpoints which we kept for ourselves are
- Can we control all the components across our application?
- Can we run DB queries?
- Can we perform data driven tests?
- Can we run tests parallelly?
- Can it support BDD?
- Can we automate api tests?
- Can we generate readable reports?
- Can we do continuous integration?
- Can we run a subset of tests from whole regression tests?
- Can we read, send emails.
- What if codeceptJS does not have native codeceptjs function for some actions which we intended to use?
So, for all the above question lets see how we have answered with sample code!!!
1. Can we control all the components across our application?
Control Type | Reading data from control | Writing/Modifying data into control | Read properties of the field | Automatable (Yes/No) | |
CheckBox | I.seeCheckboxIsChecked(); | I.checkOption(); | I.grabValueFrom() | Yes | |
I.dontSeeCheckboxIsChecked(); | I.click() | ||||
Radio Button | I.seeCheckboxIsChecked(); | I.checkOption(); | I.grabValueFrom() | Yes | |
I.dontSeeCheckboxIsChecked(); | I.click() | ||||
Dropdown | I.grabAttributeFrom() | I.selectOption(locator,value); | I.grabAttributeFrom() | Yes | |
I.waitForText() | I.waitForText() | ||||
Search button | I.grabAttributeFrom() | I.fillField() | I.grabAttributeFrom() | yes | |
I.clearField() | |||||
Button | I.grabTextFrom() | N/A | yes | ||
I.grabAttributeFrom() | I.grabAttributeFrom() | ||||
I.executeScript(function() { | I.executeScript(function() { | ||||
// javascript code to read/write/execute | // javascript code to read/write/execute | ||||
}); | }); | ||||
Date Picker | I.grabValueFrom() | I.fillField() | I.grabAttributeFrom() | Yes | |
I.clearField() | |||||
input field | I.fillField() | I.grabAttributeFrom() | Yes | ||
I.grabAttributeFrom() | I.clearField() | I.executeScript(function() { | |||
I.executeScript(function() { | // javascript code to read/write/execute | ||||
// javascript code to read/write/execute | }); | ||||
}); | |||||
Table | I.grabTextFrom() | Normally not done but added in a dialog. | I.grabAttributeFrom() | Yes | |
file(upload/download) | N/A | I.attachFile() //for upload | N/A | Yes | |
I.click() //for download | |||||
link button | I.grabTextFrom(element locator); | I.click(element locator) | I.grabAttributeFrom(locator,attribute); | Yes | |
form (to fill extra fields) | I.grabAttributeFrom() | I.fillField() | I.grabAttributeFrom() | Yes | |
I.clearField() | |||||
I.click() | |||||
Alert popup | N/A | acceptPopup() | N/A | Yes | |
cancelPopup() | |||||
Tooltip(Mouse hover) | I.grabAttributeFrom() | I.grabAttributeFrom() | Yes | ||
From the above, we can conclude at least we could automate most of the components which are used across application.
2. Can we run DB queries?
To make a connection:
const database: SQLDatabase = initSqlServer();
export = database;
function initSqlServer(): SQLDatabase {
const sqlServer = require(‘./SqlServer’);
sqlServer.init({
server: TestConfig.database.server || ‘localhost’,
port: TestConfig.database.port || 1433,
database: TestConfig.database.database || ‘Test’,
user: TestConfig.database.user || ‘sa’,
password: TestConfig.database.password,
});
For Oracle connection use oracle.init and require(‘./oracle’)
Execute the supplied (INSERT, UPDATE or DELETE) statement:
execute(statement: string): Promise<number> {
return new Promise((resolve, reject) => {
mssql
.connect(this.config)
.then((connection: any) => {
connection
.request()
.query(statement)
.then((result: number) => {
mssql.close();
resolve(result);
})
.catch((err: any) => {
mssql.close();
reject(err);
});
})
.catch((err: any) => {
reject(err);
});
});
}
Similar functions will be written for Executes the supplied query, Execute the supplied query that should result in a single value, Gets the meta data for the columns of the supplied table.
Example:
await database.execute(UPDATE test SET url='${prefix}${postfix}'
);
3. Can we perform data driven tests?
Yes, we have created data sets in JSON and we read the dataset in our code to work on data driven tests.
import { Pageobject } from ‘./pageobjects;
const data = JSON.parse(fs.readFileSync(actualFilePath).toString());
const PO = new Pageobject(); /* Page Object */
Data(data.users).Scenario(‘Test authenticator @regression, (I: CodeceptJS.I, current: any) => {
I.amOnPage(‘current.url’)
I.fillField(PO.Username, current.name);
I.fillField(PO.Password, secret(current.password)); /* Click on login button */
I.click(PO.submit);
}
4. Can we run tests parallelly?
Yes, we can always run them in chunks. We also can run them on multiple browsers
multiple: {
parallel: {
// Splits tests into 2 chunks
chunks: 2
}
}
5. Can it support BDD?
Yes, with help of Gherkin integration
Given(/I have product with $(d+) price/, (product) => {
I.amOnPage(‘/products’);
I.selectoption(productcode, product);
I.click(‘Add to cart’);
});
When(‘I go to checkout process’, () => {
I.click(‘Checkout’);
});
Then(‘I should see that total number of products is {int}’, (Value) => {
I.see(Value, ‘.cart’);
});
Then(‘my order amount is ${int}’, (total) => {
I.see(‘Total: ‘ + Total);
});
6. Can we automate api tests?
This is our favorite part, where it proves that we can automate api testing. According to codeceptJS developer it is meant for automation of rest api’s. However, we were able to do automation of SOAP calls using third party npm packages integrated to our framework.
Scenario(‘@api_001 test POC’, async (I) => {
let res = await I.sendPostRequest(data.baseUrl, body)
let expectedResponse = expectedData.api001
I.assertEqual(await I.grabValueByXmlTagName(res, “Test1”), expectedResponse.melding);
I.assertEqual(await I.grabValueByXmlTagName(res, “Test2”), expectedResponse.succes);
})
7. Can we generate readable reports?
Codeceptjs have integration to allure reporting, and other reporting options like Mochawesome, cli, xml reports etc…
Sample Mochawesome Report:
Sample Allure Report
8. Can we do continuous integration?
Codeceptjs gives us flexibility to integrate with git, Jenkins, TeamCity and Code Fresh
Below is sample screenshot for Jenkins Integration.
9. Can we run a subset of tests from whole regression tests?
Yes, with the help of tags in the tests we can run module wise, whole regression suite, or smoke tests or combination of tests
Ex: –grep ‘(?=.@regression)(?=.@smoke3)’
10. Can we read, send emails?
We can read and send emails from mails slurp, however mail slurp account is paid account for some of the features. However, we used help of mailhog to intercept email communication from our application.
import { MailhogClient, Email } from ‘mailhog-awesome’;
import { MailHog } from “../../../common/util/MailHog”;
const mailClient = new MailhogClient({});
async getEmailContext() {
let emailText: string
return await new Promise<string>((resolve, reject) => {
// wait for 60 second for executing below method from the start of executing the call method.
setTimeout(async function () {
let email = await mailClient.getLastEmail();
if (email != undefined) {
let lastEmail = (email.html) emailText = lastEmail.replace(/<[^>]+>|r|n|s/ig, ”)
} else throw “Url not found”;
resolve(emailText)
}, 60000);
});
}
11. What if codeceptJS does not have native codeceptjs function for some actions which we intended to use?
We always refer npm packages, native protractor functions and javascript during these situations, for example in the above example we have mailhog help to read emails
So, is it all cake walk with codeceptjs?
Absolutely not, as sometimes the functions of codeceptjs will not work as we intended them to. For example, smartwait option did not work for us, the way it was intended to, so we must prepare a whole new wrapper with protractor native commands. Synchronization of Javascript with codeceptjs.
Did we work before on codeceptjs?
In simple words, no. However, we at CoMakeIT hire people who can work on any tool, and any scripting language. We make it done. This is one of the secrets for our success.