In order to mock childComponent
only in the test for parentComponent
we need to:
- Build a skeleton stub for
childComponent
as a JS file.
- Instruct the
parentComponent
test to use this file instead of the real childComponent
.
Build a skeleton stub for childComponent
as a JS file.
- Create a folder inside the
childComponent
's folder named __mocks__
.
- Inside that folder, create a file
childComponent.js
, copying the contents of the real component into it. You should end up with a folder structure like:
lwc
childComponent
__mocks__
childComponent.js
__tests__
childComponent.test.js
childComponent.html
childComponent.js
childComponent.js-meta.xml
- Strip out everything from the new file other than the class definition and
@api
defined properties. E.g:
import { LightningElement, api } from 'lwc';
export default class ChildComponent extends LightningElement
{
@api childProperty;
@api otherChildProperty;
@api doThing() {} // include any @api annotated methods
}
- Ensure that the mocks folders are ignored when you perform a push by adding
**/__mocks__/**
into your .forceignore. E.g. it may look like:
package.xml
# LWC configuration files
**/jsconfig.json
**/.eslintrc.json
# LWC Jest
**/__tests__/**
**/__mocks__/**
Instruct the parentComponent
test to use this file instead of the real childComponent
.
- At the top of the unit test for
parentComponent
, tell Jest to use the mock version of childComponent
by calling jest.mock
passing the Salesforce style import path of the component. E.g. the top of your test file may look like:
import { createElement } from 'lwc';
import ParentComponent from 'c/parentComponent';
jest.mock( 'c/childComponent' ); // Uses the mock version of childComponent defined in the __mocks__ directory of that LWC
describe( 'c-parent-component', () => {
// your tests...
}
Stretch goal: Mocking methods that exist on the child component.
- If you need to then check that methods are called on your child component, then you can grab the child from the parent component and configure a mock function for any that you are interested in.
For example:
describe('c-parent-component', () => {
beforeEach( () => {
const element = createElement('c-parent-component', {
is: ParentComponent
});
document.body.appendChild( element );
})
it( 'Child component is called', () => {
const element = document.body.querySelector( 'c-parent-component' );
const child = element.shadowRoot?.querySelector( 'c-child-component' ); // Get the child component from the parent's DOM
child.doThing = jest.fn(); // override the method on child to be a mock. You can define its behaviour at this point too.
return Promise.resolve()
.then( () => element.shadowRoot?.querySelector( 'lightning-button' ).click() )
.then( () => {
expect( child.doThing ).toHaveBeenCalledTimes( 1 ); // check that the child method was called. You can check parameters were passed, etc.
})
});
}
Note: in general, I would use more identifying characteristics on the component other than simply the type of HTML element it is. E.g. I would add a data-id or data-name and use that instead.
(thanks to https://tigerfacesystems.com/blog/lwc-nested-component-testing for the basic solution to this problem)