This guide shows you the basics of using Polymer CLI to run unit tests, and how to accomplish various tasks and scenarios using the Web Component Tester library (the underlying library that powers Polymer CLI's testing tools).
Update your tools for 2.0. For testing with 2.0, make sure you have the latest version of the Polymer CLI.
Overview
Polymer CLI is an all-in-one command-line interface that covers the vast majority of Polymer development tasks, including unit testing. The underlying library that powers Polymer CLI's unit testing tools is called Web Component Tester.
Web Component Tester is an end-to-end testing environment built by the Polymer team. It enables you to test your elements locally, against all of your installed browsers, or remotely, via Sauce Labs. It is built on top of popular third-party tools, including:
- Mocha for a test framework, complete with support for BDD and TDD.
- Chai for more assertion types that can be used with your Mocha tests.
- Sinon for spies, stubs, and mocks.
- Selenium for running tests against multiple browsers.
- Accessibility Developer Tools for accessibility audits.
Quick start
For demonstration purposes, this guide shows you how to install Polymer CLI and initialize an element project. You'll then use this project to learn how to add and run unit tests.
-
Install Polymer CLI. Follow the directions in Install Polymer CLI to get started.
-
Create an element project. This guide assumes that your element project directory and your element are both named
my-el
. -
cd
to the base directory of your project. -
Run
ls
.You'll see that your element project contains a directory called
test
. This is where all of your unit tests should be stored.When you run
polymer test
, Web Component Tester automatically searches for atest
directory and runs any tests it finds in there. If you use another directory name, you'll need to specify it when you runpolymer test
. -
Open up
test/my-el_test.html
to see an example of a basic unit test. -
Run the test.
polymer test
Web Component Tester automatically finds all of the browsers on your system and runs your tests against each one. To run your tests against a single browser, use the
-l
(or--local
) argument:polymer test -l chrome
If you receive errors about testing on Safari, see Set up testing on Safari.
Run tests interactively
You can also run your tests in the browser. This allows you to use the browser's DevTools to inspect or debug your unit tests.
For example, using Polymer CLI and the example element project created in Quick start above, first you would start your server:
polymer serve
And then, to run the basic my-el_test.html
unit test in the browser, you
would open a web browser and go to the following URL:
localhost:8080/components/my-el/test/my-el_test.html
Creating tests
Now that you've got the basics down of using Polymer CLI to run tests, it's time to start creating them.
This section of the doc shows you how to accomplish various tasks or scenarios while implementing your unit tests.
Asynchronous tests
To create an asynchronous test, pass done
as an argument to the test function
and then call done()
when the test is complete. The done
argument is a
signal to Mocha that the test is asynchronous. When Mocha runs the test,
it waits until the test code invokes the done()
callback. If the done()
callback isn't invoked, the test eventually times out and Mocha reports the test
as a failure.
fireEvent() {
this.dispatchEvent(new CustomEvent('test-event', {detail: 'tested!'}));
}
test('fires an event', function(done) {
myEl.addEventListener('test-event', function(event) {
assert.equal(event.detail, 'tested!');
done();
});
myEl.fireEvent();
});
Prevent shared state with test fixtures
Test fixtures enable you to define a template of content and copy a clean, new instance of that content into each test suite. Use test fixtures to minimize the amount of shared state between test suites.
To use a test fixture:
- Define the test fixture template and give it an ID.
- Define a variable in your test script to reference the template.
- Instantiate a new instance of the fixture in your
setup()
method.
<test-fixture id="my-el-fixture">
<template>
<my-el prop1="test value">
</my-el>
</template>
</test-fixture>
<script>
suite('<my-el>', function() {
var myEl;
setup(function() {
myEl = fixture('my-el-fixture');
});
test('sets the "prop1" property from markup', function() {
assert.equal(myEl.prop1, 'test value');
});
});
</script>
Create stub methods
Stubs enable you to replace default implementations with custom methods. This is useful for catching side effects.
setup(function() {
stub('paper-button', {
click: function() {
console.log('paper-button.click called');
}
});
});
You don't have to use stubs directly on individual elements. You can override the implementation for all elements of a given type.
Create stub elements
Use stub elements to test elements in isolation. For example, if one of your tests depends on another element to return data, rather than importing the other (possibly unstable) element into your tests, you can implement a stub of the other element that always returns consistent data.
Use replace()
to create stub elements.
setup(function() {
replace('paper-button').with('fake-paper-button');
});
For example, using the sample replace()
above and the element below:
<dom-module id='x-el'>
<template>
<paper-button id="pb">button</paper-button>
</template>
</dom-module>
At test runtime, the content template would be stamped out as:
<dom-module id='x-el'>
<template>
<fake-paper-button id="pb">button</fake-paper-button>
</template>
</dom-module>
The attributes and content of the element are preserved, but the tag is replaced with the specified stub tag.
Avoid dependencies on element name where possible. Using a stub element will break any test or element code
that relies on knowing the element name. (For example, querySelector('paper-button')
won't return a
fake-paper-button
element. It's safer to rely on the element's ID.
Because the method is called within setup()
, all of the changes are
reverted at the end of each test.
AJAX
Web Component Tester includes Sinon, which enables you to mock XHR requests and create fake servers.
Below is an example of a simple XHR unit test suite for
<iron-ajax>
.
Check out Sinon's documentation for more in-depth examples.
<!-- create test fixture template -->
<test-fixture id="simple-get">
<template>
<iron-ajax url="/responds_to_get_with_json"></iron-ajax>
</template>
</test-fixture>
<script>
suite('<iron-ajax>', function() {
var ajax;
var request;
var server;
var responseHeaders = {
json: { 'Content-Type': 'application/json' }
};
setup(function() {
server = sinon.fakeServer.create();
server.respondWith(
'GET',
/\/responds_to_get_with_json.*/, [
200,
responseHeaders.json,
'{"success":true}'
]
);
});
teardown(function() {
server.restore();
});
suite('when making simple GET requests for JSON', function() {
setup(function() {
// get fresh instance of iron-ajax before every test
ajax = fixture('simple-get');
});
test('has sane defaults that love you', function() {
request = ajax.generateRequest();
server.respond();
expect(request.response).to.be.ok;
expect(request.response).to.be.an('object');
expect(request.response.success).to.be.equal(true);
});
test('has the correct xhr method', function() {
request = ajax.generateRequest();
expect(request.xhr.method).to.be.equal('GET');
});
});
});
</script>
Note: The example above uses Chai's expect
assertion style.
Run a set of tests
To run a set of tests, create an HTML file and call loadSuites()
. When
running Web Component Tester, specify the path to the HTML file as the first argument
(for example, wct test/my-test-set.html
.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script src=”../bower_components/webcomponentsjs/webcomponents-lite.js”></script>
<script src=”../bower_components/web-component-tester/browser.js”></script>
</head>
<body>
<script>
WCT.loadSuites([
'basic.html',
'async.html'
]);
</script>
</body>
</html>
The argument to loadSuites()
should be an array of strings. Each string
should be a relative URL to a test suite. You can configure your tests
using query strings in the URLs. See Test shadow DOM
for an example.
Hybrid elements: test local DOM
For hybrid elements, use Polymer's DOM API to access and modify local DOM children.
test('click sets isWaiting to true', function() {
myEl.$$('button').click();
assert(myEl.isWaiting, true);
});
Note: myEl.$$('button')
returns the first button
element encountered
in the local DOM of myEl
.
Test DOM mutations
Always wrap your test in flush
if your element template contains a template
repeater (dom-repeat
) or
conditional template (dom-if
),
or if your test involves shadow DOM mutation. The shady DOM polyfill lazily performs these
operations in some cases for performance. flush
ensures that asynchronous
changes have taken place. The test function should take one argument, done
,
to indicate that it is asynchronous, and it should call
done()
at the end of flush
.
suite('my-list tests', function() {
var list, listItems;
setup(function() {
list = fixture('basic');
});
test('Item lengths should be equal', function(done) {
list.items = [
'Responsive Web App boilerplate',
'Unit testing with Web Component Tester',
'Offline support with the Platinum Service Worker Elements'
];
// Data bindings will stamp out new DOM asynchronously
// so wait to check for updates
flush(function() {
listItems = list.shadowRoot.querySelectorAll('li');
assert.equal(list.items.length, listItems.length);
done();
});
});
)};
Test with polyfills
To test out how a test suite behaves when the browser runs the polyfill, create a test set and pass options in your a query string when Web Component Tester loads your test suites.
WCT.loadSuites([
'basic-test.html',
'basic-test.html?wc-ce&wc-shadydom&wc-shimcssproperties'
]);
This sample runs basic-test.html
twice, once using native APIs (where the browser supports them),
and once using using all of the polyfills.
Automated testing in the cloud
It's important to get a good testing setup in place for your project as early as possible. Using services like Travis for continuous integration, and Sauce Labs for cross-browser testing means you can be confident that changes you push to your project will work well on different platforms and devices. For guidance on setting up these tools check out the Polycast below.
Set up testing on Safari
Safari 10 has built-in support for testing, but it must be manually enabled. Safari 9 may require a you to install a browser extension.
To enable testing on Safari 10:
-
If you don't see the Develop menu in the Safari menu bar, enable it:
- Select Safari > Preferences, then click the Advanced tab.
- Check the Show Develop menu checkbox.
-
Select Develop > Allow Remote Automation in the menu bar.
-
You may need to authorize
safaridriver
to launch thewebdriverd
service which hosts the local web server. To do this, startsafaridriver
once manually:/usr/bin/safaridriver
Complete the authentication prompt.
More information: WebDriver support in Safari 10
To enable testing on Safari 9:
- Download the legacy SafariDriver extension for Selenium (
SafariDriver.safariextz
). - Double-click the extension to install it in Safari.
- Exit Safari.
More information:
- Selenium SafariDriver page. Note that the link for the SafariDriver on this page is unhelpful.
Learn more
Polymer Summit 2015 video on testing:
The Web Component Tester README has more in-depth information about Web Component Tester usage.