Moving from PhantomJS to Chrome
We used PhantomJS for automated UI testing for some time. It successfully helped us to be sure that our Polymer-component based pages behaved correctly. At least that’s what we thought.
We used Mocha for testing with mocha-phantomjs package. Our tests looked pretty much the same: we created some components, triggered events and evaluated the results. Some of the tests were asynchronous, some not. With each feature the amount of tests got higher, and we made sure all of them passed.
Migration from PhantomJS to headless chrome was very seamless. In the bottom
line, all I did was changing mocha-phantomjs
to mocha-headless-chrome
in a
couple of places. The interesting part came later, when I saw the results of
the same tests we ran before.
The first interesting thing I found was that PhantomJS did not mention the assertion failure in the below code:
it('should fail with false condition', function() {
const condition = false;
setTimeout(function() {
assert(condition);
}, 10);
});
// PhantomJS tests
// ✓ should fail with false condition
//
// 1 passing (4ms));
Of course, you say, it’s an asynchronous test, you missed the done()
callback! And you are 100% percent right. But… This code slipped into
production and was left there unnoticed. Until we made the move:
// PhantomJS tests
// ✓ should fail with false condition
//
// 1 passing (12ms)
//
// [Error: AssertionError]
Did you notice the AssertionError
on the last line? That was a clear sign
that something was wrong. After being pointed directly to a bad test, it was a
matter of minutes to figure out it was broken, and fix it:
it('should fail with false condition', function(done) {
const condition = false;
setTimeout(function() {
assert(condition);
done();
}, 10);
});
// 1) should fail with false condition
// [Error: AssertionError]
//
// 0 passing (23ms)
// 1 failing
//
// 1) PhantomJS tests should fail with false condition:
// Uncaught Error: Uncaught AssertionError: false == true
Another issue, more pleasant, was ES6 support. It
was really frustrating to discover that I accidentally used let
or
Object.assign
or shorthand Object
initializer
somewhere inside a component’s code. In PhantomJS, it broke the entire
test
silently, without showing related errors.
<!-- component code -->
<dom-module id="meh-meh">
<template>
<div>
{{ item }}
</div>
</template>
<script type="text/javascript">
Polymer({
is: 'meh-meh',
properties: {
item: {
value: function() {
// note the 'let' (es2015 syntax)
let thing = 'thing';
return thing;
},
},
},
});
</script>
</dom-module>
// test code
it.only('should have el.$ object', function() {
const el = document.createElement('meh-meh');
el['item'] = 'an item';
document.body.appendChild(el);
// $ is created by Polymer
const condition = el.$ !== undefined;
assert(condition);
});
In PhantomJS this test failed. In headless chrome, it worked fine.
The move from PhantomJS to headless chrome for UI testing enabled us to
consider using ES6 for our client side code. It gave us an indication that
something was wrong with our existing tests. It made client side tests faster.
And, of course, it just felt good to improve our system’s build process with a
small change in package.json
.