Things I learned after writing tests for JS and HTML page
In recent weeks, I want to learn about writing tests using Jest, Mocha or other stuffs. Unfortunately, I don't have any React apps that want to test these days but I have chances to figure out how to test HTML page with pure JavaScript.
I found most tutorials are using Jest with React or other JavaScript frameworks. Is it really possible to test HTML page with Jest? Yes!
How to start
View the demo here for my sample HTML page. Just a simple page to fetch JSON, show a list based on it and a button to show/hide translations.
In your root folder, create a package.json
like this, and run npm install
in console.
{
"scripts": {
"test": "jest --watch",
"coverage": "jest --coverage"
},
"devDependencies": {
"jest": "^23.6.0"
}
}
When you finished, you can start testing your app! Create a file <YOUR-FILENAME>.spec.js
and start testing like:
const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');
jest
.dontMock('fs');
describe('button', function () {
beforeEach(() => {
document.documentElement.innerHTML = html.toString();
});
afterEach(() => {
// restore the original func after test
jest.resetModules();
});
it('button exists', function () {
expect(document.getElementById('disable')).toBeTruthy();
});
});
Save it and run npm test
!
What I learned
1. Difference between testing HTML and React page
In React, you can use Enzyme to shallow
the component and get state for testing. But in HTML and JavaScript page, the only thing you can test is the classes, content and function output. When you are used to test by states, it might be not so convenient to test HTML page.
As pure JS did not export anything for Jest to test, you also need to add this at the end:
if (typeof exports !== 'undefined') {
module.exports = {
getItem,
setItems,
triggerItem
};
}
Then Jest can import/export functions for testing.
For HTML, you cannot directly import it like React component. You need to add this snippet before tests, to import the whole HTML:
const fs = require('fs');
const path = require('path');
const html = fs.readFileSync(path.resolve(__dirname, '../index.html'), 'utf8');
jest.dontMock('fs');
or write inline HTML inside tests. For example:
const html = document.createElement('div');
html.innerHTML = `<div class="lang-grid" id="language">This is a sample</div>`;
document.body.appendChild(div);
2. Special way to test asynchronous code
It is still easy to test basic thing, like checking the menu button will appear in mobile view using Jest. Like:
it('menu button exists', function () {
expect(document.getElementById('menu-btn')).toBeTruthy();
});
But for asynchronous code, like Promise, you need to use different approach.
The most important thing is to add done()
in each test.
it('get same items from json', function (done) {
fetch.mockResponse(JSON.stringify(json))
const {getItem} = require('../scripts/main.js');
getItem().then(res => {
expect(res).toEqual([{
"phase": "Entschuldigung!",
"trans": "Excuse me. [as in may I have your attention]."
},
{
"phase": "Sprechen Sie Englisch?",
"trans": "Do you speak English?"
}])
expect(res.length).toEqual(2);
done();
})
.catch(err => console.log(err))
});
Just like what Jest documentation said, it is important to add done()
in the test. Otherwise, it may have wrong results.
After you added done()
, it will wait till your async call to be resolved and get the expected result.
3. Check coverage using Jest
Jest has built-in coverage function, you can call it using jest --coverage
. Then you can see your reports in coverage/lcov-report/index.html
. The chart is very useful and inform you which code have not tested.
Why it is not 100% in branches? Because I skipped the test of exporting module statement at the end.
I have not use coverage tool before, that's why I am motivated when I saw my code changed from red to green!
Is testing fun?
Well, it may not be fun, but surely is satisfactory when I saw my code changed from RED to GREEN.
Do you have any suggestions to my flow? Or any ideas on testing? Feel free to drop me a line here :)