Skip to content

feat: Support for mocking methods on Stencil components in unit tests #4232

Description

@cmardyla

Prerequisites

Describe the Feature Request

Stencil supports mocking function calls from utility functions or classes (see Stencil docs on mocking) but as far as I can tell, there is no way to mock a method call on a web component.

For example, say you have two Stencil components, MyComponent and MyOtherComponent. MyOther component contains a MyOtherComponent element, for example

<my-component>
    #shadow-root
    <my-other-component></my-other-component>
<my-component>

Additionally, suppose MyComponent and MyOtherComponent each define a method, foo. The implementation of foo on MyComponent looks like this

@Method()
async foo() {
    await this.#myOtherComponentElementRef.foo();
}

The easiest way to unit test foo on MyComponent would be to write a test that spies on the MyOtherComponent.foo call, and assert that the spy is called when foo is called on MyComponent. However, this is not possible today - this support is what I'm requesting a feature for.

I'm not sure if this would be a feature request or a bug report, as I don't know if this functionality was ever officially supported. I've tried a few different ways to mock a method on a web component, with various errors (see the example repo linked in the "Related Code" section for code examples of my attempts)

Describe the Use Case

This feature would make it much easier to unit test complex Stencil components that are composed of other Stencil components.

Describe Preferred Solution

I could see this implemented in a similar way to how utility functions can be mocked, for example like this:

const mockFoo = jest.fn();
jest.mock("../my-other-component/my-other-component", () => ({
    foo: mockFoo
}));

// Other imports and test setup

it("foo() calls foo on MyOtherComponent", async () => {
  const page = await newSpecPage({
    components: [MyComponent, MyOtherComponent],
    html: `<my-component></my-component>`
  });

  const myComponent = page.root as HTMLMyComponentElement;
  await myComponent.foo();
  expect(mockFoo).toHaveBeenCalled();
});

Describe Alternatives

It's possible to test a situation like this indirectly (by testing for the effects of foo have occurred, rather than just testing that foo was called) but this can unnecessarily complicate unit tests. It's also not really "unit" testing code.

Related Code

I've set up an example repo illustrating the issues with mocking methods today. In the repo, please refer to

Additional Information

No response

Metadata

Metadata

Labels

Feature: Want this? Upvote it!This PR or Issue may be a great consideration for a future idea.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions