vue-test-utils: Named slots' content is not generated for stubbed components via shallowMount
What problem does this feature solve?
Higher-order components make use of named slots. It’s possible to test higher-order components via shallowMount because named slot content can be mocked, but it’s not possible to test components which consume higher-order components because their generated named slot output is not available in the stubbed content.
https://github.com/vuejs/vue-test-utils/pull/782 added support for outputting default slot content, but not named slots.
To be clear, an example of a component which needs this support is:
<!-- myComponent.vue -->
<template>
<HigherOrderComponent foo="bar">
default slot content
<template v-slot="header">header slot content</template>
<template v-slot="footer">footer slot content<HigherOrderComponent foo="waldo"/></template>
</HigherOrderComponent>
</template>
Currently, this is stubbed out to only:
<higherordercomponent-stub foo="bar">default slot content</higherordercomponent-stub>
The end user experience should be that components which generate named slot content “just work” when testing with shallowMount.
What does the proposed API look like?
The API would not change.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 2
- Comments: 18 (8 by maintainers)
For those looking for a solution now, manually stubbing out the component seems to render the child slots:
It wasn’t just a case of “I didn’t merge it”. Looks like there was some conflict and failing tests here which were not resolved: https://github.com/vuejs/vue-test-utils/pull/1309
I think we need to support both behaviors to make this work for everyone. Some people will expect a true shallow mount, others want stubbed components to render slots.
I am pretty focused on getting v1 of this lib released, vue-jest 4 and 5, and VTU next, so I cannot work on this right now. If you (or anyone else reading this) would like to take a stab at this (or anyone else) feel free to do so. What I’d like to see:
Or
I don’t think it makes sense as a mounting option, since those are shared with
mount, though.Another alternative would be a
shallowWithStubsmethod.Either way, the API won’t impact the implementation. Does this sound appropriate? I think this will let everyone write their tests however they like.
This may work but you end up stubbing so much you aren’t really testing anything. The best way to test slots is by using
mount, you’ll get the same behavior as production and avoid writing tests that give you false confidence.I said “for the sake of example”, the focus here is not the number of components. You said “just stub out the troublesome component”, and I’m saying that it could be 2, 5, a dozen, or any arbitrary number of components that need to be stubbed out when using
mount, with the worst case being having to stub out every other component except for the one being tested. For large apps with deep trees, this can lead to stubbing out large parts of the app just to write a “unit test”, which is starting with an integration test and stubbing out everything that’s causing problems until it effectively becomes a unit test.The user story is, “as a developer writing a unit test, I want to verify that my component is passing the proper data to a child component.” Data is defined as anything and everything that can be passed to the child component, which AFAIK is props, attributes, and templates. So given this example:
It’s already been done, but you closed it: https://github.com/vuejs/vue-test-utils/pull/1309 From your comment in the PR:
To reiterate the above example:
We can test that props are properly passed to children using
.props(). We can test that attributes are properly passed to children using.attributes(). We can test that the default slot is properly passed to children using.text()or.html().Without the
stubs: { Component }workaround, we cannot test that named slots are passed properly. Whether stubs should render content inside them is really not the point here, it’s that there’s no other way to test slots otherwise in a unit test. If there was something likechildWrapper.templates(), then I’m all for stubbing out the child completely, but until that happens, checkingchildWrapper.text()orchildWrapper.html()remains the only way to verify slots. Usingmountis not a solution because, once again, it turns a unit test into an integration test.Sure, my original comment was simply to provide a solution that works today. This issue has been reported multiple times and even has a PR for it, so it’s definitely important to at least some of us, and we can’t wait around for the Vue 3 integration.
I already addressed that, but to quote @souldzin, who put it much better than I can: https://github.com/vuejs/vue-test-utils/issues/1498#issuecomment-612988985
Yes.
For the sake of example, you have 1 million unique child components, each of which does an AJAX call in their
created()hook that takes 2 seconds to complete. Do you stub every one out? And after you do, what’s the difference betweenmountandshallowMountif you’ve stubbed everything out except the one component you’re testing?To be clear, to properly test a component, its shallowMount rendering should be something like this:
Not simply this:
That’s the intention. To use OP’s example, I’m writing a unit test for
myComponent.vue, so I wantHigherOrderComponentto be stubbed out. I also want to test thatmyComponentis sending down the correct template data toHigherOrderComponent.Right now, the default template is being rendered in the stub with
shallowMount, but named templates are not. This doesn’t make sense. Manually stubbing outHigherOrderComponentfixes it so that named templates are rendered in the stub as well.Using
mountmeans that the unit test is no longer a unit test. IfmyComponentwas the root component, by usingmountthe entire app is instantiated. This is explicitly mentioned in the official docs:https://vue-test-utils.vuejs.org/guides/common-tips.html
I have closed the PR that supposedly solved this problem.
We will revisit shallowMount and how it should render things for the Vue 3 integration. At this point, I don’t think we will change how shallowMount is working.
I will make a new issue when we revisit shallowMount for v3 integration and invite everyone’s input that point.