Stencil makes it easy to unit test your component using Jest and the Stencil unit testing framework.
The testing framework requires very little configuration and has a minimal API consisting of two functions:
render() and flush(). The Stencil unit testing framework can be used to test the rendering of the component
as well as the methods defined on the component class.
Allowing a Stencil component project to run unit tests requires a small amount of configuration in the package.json
file. All of this configuration is included with the Stencil App Starter and the Stencil Component Starter so if you
use one of those templates to start your project, you should not have to add anything. This information is presented
here primarily for informational purposes.
Jest is installed as a development dependency:
"devDependencies": {
...
"@types/jest": "^21.1.1",
"jest": "^21.2.1"
},
NPM scripts are set up in order to run the tests:
"scripts": {
...
"test": "jest --no-cache",
"test.watch": "jest --watch --no-cache"
},
Jest is configured to find test files and to use the Stencil preprocessor script to compile the sources:
"jest": {
"transform": {
"^.+\\.(ts|tsx)$": "<rootDir>/node_modules/@stencil/core/testing/jest.preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json",
"jsx"
]
}
The Stencil testing framework API contains two functions that are used to render components for testing:
render({ components: [], html: string }) - The render() function takes a list of components and an HTML snippet
and returns the promise of the rendered HTML element.
flush(element) - The flush() function is used to refresh the rendering of an element after property changes are made.
This function returns a promise that is resolved when the flush is complete.
Both of these function operate asynchronously.
A common testing pattern when rendering is to render() the component in the beforeEach() for a suite of tests. Each
test case then modifies the element and uses flush(element) to refresh the node.
Use the render() function to initially render a component.
This function takes a configuration object with two parameters:
components: a list of components the renderer needs to know about. Generally, this only needs to contain the
component being tested. Child components can also be included if you need to have them rendered for your test
but this is not a requirement otherwise.
html: an HTML snippet used to render the component. Usually this just looks like <my-component></my-component>.
This function returns a promise that is resolved with the rendered HTML element.
beforeEach(async () => {
element = await render({
components: [MyName],
html: '<my-name></my-name>'
});
});
Use the flush() function to re-render the node as needed. This is typically done after changing property values
on the component.
it('should work with both the first and the last name', async () => {
element.first = 'Peter'
element.last = 'Parker';
await flush(element);
expect(element.textContent).toEqual('Hello, my name is Peter Parker');
});
Since the rendered element is an HTMLElement, you can use methods and properties from the HTMLElement interface in order to examine the contents of the element.
Let's say that instead of printing the first and last names, our component had to split the names apart on spaces and print a list of each part of the name. We could write a rendering test for that as such:
it('should least each part of the name breaking on spaces', async () => {
element.first = 'Pasta Primavera';
element.last = 'O Shea Buttersworth';
await flush(element);
const list = element.querySelector('ul');
expect(list.children.length).toEqual(5);
expect(list.children[0].textContent).toEqual('Pasta');
expect(list.children[1].textContent).toEqual('Primavera');
expect(list.children[2].textContent).toEqual('O');
expect(list.children[3].textContent).toEqual('Shea');
expect(list.children[4].textContent).toEqual('Buttersworth');
});
Anything that you can use on an HTMLElement you can use in the tests.
To test the component's methods, simply instantiate an instance of the component and call the methods.
it('should return an empty string if there is no first or last name', () => {
const myName = new MyName();
expect(myName.formatted()).toEqual('');
});
it('should return a formatted string if there is no first or last name', () => {
const myName = new MyName();
myName.first = 'Lucas';
myName.last = 'Kalrickson';
expect(myName.formatted()).toEqual('Kalrickson, Lucas');
});