Introduction
The idea behind gremlins testing is pretty simple: any user action on your application should not have throw a runtime error. It's like a smoke test with lots of random user's actions. This should be helpful when your project has unit tests but you're not ready yet for end to end tests.
Implementation
- Install the lib:
yarn add gremlins.js -D
- Copy gremlins to hosted directory (e.g. dist)
cp node_modules/gremlins.js/dist/gremlins.min.js dist
- Open your app in a browser
- In the browser console add gremlins to DOM
const s = document.createElement('script'); s.src='gremlins.min.js'; document.head.appendChild(s);
- Run the gremlins gremlins.createHorde().unleash()
- In result gremlins will execute buch od random events
If in console there's no error test passes
Automatisation
Of course we can not run it manually each time. To automate it we can use the puppeteer, it's a nodeJS chromium driver. After installation yarn add puppeteer -D
do something like this:
// run-gremlins.js
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const errors = [];
page.on('pageerror', (error) => {
browserErrors.push(error);
});
await page.goto('localhost:4200');
if (browserErrors.length !== 0) {
errors.forEach((e) => {
console.error('e2e error: ', e);
});
process.exit(1);
}
// TODO: insert gremlins
// TODO: run gremlins
// TODO: wait 5s
await browser.close();
})();
Enter fullscreen mode
Exit fullscreen mode
This code will run a browser (in incognito mode to have cookies cleared), open new a page on localhost:4200
, run gremlins like in the previous part and if any page error appears process will close with falsy status.
missing parts:
module.exports.insertGremlins = async (page) => {
await page.evaluate(() => {
const s = document.createElement('script');
s.src='gremlins.min.js';
document.head.appendChild(s);
});
};
module.exports.runGremlins = async (page) => {
await page.evaluate(() => {
window.gremlins.createHorde().unleash()
});
};
module.exports.wait = (time) =>
new Promise((resolve) => {
setTimeout(resolve, time);
});
Enter fullscreen mode
Exit fullscreen mode
Verification
To be sure that gremlins are gonna catch an error, add something like this to the app: setTimeout(() => { throw new Error('debug'); }, 3000);
. If command node run-gremlins.js
will exit with falsy code then the tester works correctly :)
Run on docker
Best way to be sure that tester is working on other environment (like CI) is wrap it into a docker container. Unfortunately we need to install some dependencies in the container.
FROM node:16.13.1 # Specific version, not the lastest
RUN ["apt-get", "update"]
RUN apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils -y
RUN apt-get update
&& apt-get install -y wget --no-install-recommends
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
&& apt-get update
&& apt-get install -y google-chrome-unstable --no-install-recommends
&& rm -rf /var/lib/apt/lists/*
&& wget --quiet https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh -O /usr/sbin/wait-for-it.sh
&& chmod +x /usr/sbin/wait-for-it.sh
Enter fullscreen mode
Exit fullscreen mode
To allow root
user run puppeteer add --no-sandbox
flag into run-gremlins.js
file:
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
});
Enter fullscreen mode
Exit fullscreen mode
In this case your local web application has to run in the same container, however you can add --network=host
to the previous command to allow gremlin-tester to communicate with a localhost.
I'm using it, it saves a lot of my time. Maybe someone else will to take advantage of it.