When building a component and checking for the events emitted by this component we use component.emitted('eventX')[0] and do the tests. On the next test, we build the component again and use the same assertion.
When the component is more complex, it would be better to just reset the emitted event instead of rebuilding the component. This is common behavior on testing libraries for function (sinon.js, Jest, for example).
component.resetEmitted() would be enough.
I can try doing it if this is a desired feature.
I'd like this feature to be implemented, meanwhile I'm doing the following to reset/clear the emitted events:
component._emitted[event] = []
Hi! Sorry it took so long for someone to follow up on this.
I see why this is a valid concern, but couldn't this be as easily solved with a factory function? Eg:
const createWrapper = () => {
return mount(MyComplexComponent, {
// ... a ton of options
})
}
Then you test becomes:
test('It works', () => {
const wrapper = createWrapper()
// that's it!
})
You can also make createWrapper take arguments if you want to set data, props etc.
Likely you want to reset the _entire_ component, not just the the emitted events? It seems more ideal to have an entirely fresh copy of the component for each test. There is a good article about it in the Vue.js cookbook: https://vuejs.org/v2/cookbook/unit-testing-vue-components.html#Real-World-Example
What do you think?
Hey @lmiller1990 ! This is an option, and that's how I was doing it, but recreating the component every time is a little over kill if we just want to test a behavior triggered from the same action again and again, for example.
In the case I'm talking about, I'd like to reset just the emitted events, like jest.clearAllMocks() or sinon's spy.resetHistory().
Sorry I don't have a real use case to include here right now.
Thanks for the reply.
If there is a real performance bottleneck with recreating the component, I think that is something we should fix.
At this point, I don't think this is feature something we will be including in Vue Test Utils. If this was to become a feature one day, I'd be in support of a resetComponent method, that would reset _everything_ to the base state. Since that can be accomplished with a factory function, though, this isn't something we will be including.
Thanks for your understanding!
I believe this is something that should be implemented, not only it would be useful, it would also follow the same functionality of other stub/spy test frameworks (which include this for a reason).
We shouldn't be modifying component._emitted[event] = [] directly to achieve this reset functionality...
I am also against this. You should not have to reset the emitted counter. If you do, then this is another test.
End of story.
Consider the following test:
it('can be disabled', () => {
const wrapper = mount(ActionButton)
const button = wrapper.find('button')
button.trigger('click')
// Expect the button to be active and respond to clicks.
expect(wrapper.vm.disabled).toBe(false)
expect(wrapper.emitted('click')).toBeTruthy()
// Disable the button and expect it not to respond to clicks.
wrapper.setProps({ disabled: true })
button.trigger('click')
expect(wrapper.vm.disabled).toBe(true)
expect(wrapper.emitted('click')).not.toBeTruthy()
})
It's a valid use-case to make sure an event gets emitted (or not emitted) based on a prop change.
This currently doesn't work because the emitters somehow are stored.
Changing it to expect(wrapper.emitted('click').length).toBe(1) works but is (in my opinion) very counter intuitive and now depends on the author tracking when and where and how many events were emitted.
Tracking how many times emit was called should not be too difficult in a single unit test. Or are you using the same wrapper instance for many tests?
Alternatively, you could have two tests.
it('does emit when enabled', () => {
const wrapper = mount(ActionButton, {
propsData: {
disabled: false
}
})
const button = wrapper.find('button').trigger('click')
expect(wrapper.emitted('click')).toBeTruthy()
})
it('does not emit when disabled', () => {
const wrapper = mount(ActionButton, {
propsData: {
disabled: true
}
})
const button = wrapper.find('button').trigger('click')
expect(wrapper.emitted('click')).toBeFalsy()
})
Now you get more granular failures, since each test only deals with one situation.
I personally feel messing with the internals of a component during a test isn't not ideal, since that is a behavior Vue does not support (so you are testing against a non production behavior).
I am really surprised to see the resistance to this. I agree with the comment above that this is very much like resetting the calls on a spy. I think one of the best use cases that can be put forth for this when you consider something like this for your tests:
describe('test my component'), () => {
let wrapper;
beforeAll(() => {
wrapper = mount(...);
});
beforeEach(() => {
wrapper.resetEmitted();
});
});
When I have 10,000+ unit tests for my app, it matters if the test run can be shaved down in time by not recreating the mounted component for each and every test. This is a pattern I have used over and over again when using Spys. Emit is no different than a Spy. We want to knot that it got called, and each test run should have a fresh copy of the, without having to do too much work. (mounting the component for each and every test seems like too much work, even if it is fairly fast.)
So how do you reset your component between tests? How do you ensure each prop, data is untouched? Wont that be more work than just re-mounting it? Just thinking out loud here.
There is often a whole series of tests that can be run on an object without it corrupting the object and needing to be reset. And sure, for those tests that need a fresh copy of the component, then create a new one.
Here is an example, let's assume that there are a number of getter values that are used to control how the object looks and those react and change based on the value of the store and how that store might change.
I can change the store over and over again and test those values are correct based on the current value of the store without having to remount the component. And in some of those, the component might be emitting events based on how the store changes and I want to test that, so all I have to do is reset the emit counter, change the store appropriately and verify the emit happens.
I feel like this could be a slippery slope - won't we then need to condition resetData, resetComputed, resetProps as well?
A previous comment said you can just do component._emitted[event] = []. Or say, component._emitted = [] for the full reset. The fact that this feature is already available in a one liner seems like it should be enough - are you just asking for a prettier syntax around component._emitted[event] = []?
If there is a performance issue, related to remounting components, we should definitely look into that, and find out where the bottleneck is.
@lmiller1990 That work around didn't work for me. I don't know why, but it didn't work. I would have to dig into it again to see why it didn't.
I disagree that this has to lead to a slippery slop of resetting the other things. In fact, the other things are already spelled out in different guides as far as I remember.
I think the best way to think of this as being analogous to Spies in jest/jasmine. Why did Jest/Jasmine provide a reset() function on a spy? Why didn't they just say "just recreate the spy. We don't want to provide a way to reset the call counts"? I don't have insight into what they were thinking at the time, but it is exactly the same case as emit. This is something that the component can do over time and you could choose to reset it in the beforeEach().
Persisted state between unit tests is never a good idea - you should always remount your component for every test.
I'm happy to help write some code to reset the emitted events that people can add into the projects (like a plugin). I don't think this belongs in core - the API is already too large and complicated as is. We are already trying to cut back on features for the Vue 3 compat, adding new APIs at this point isn't something I think is ideal.
I think we should consider exposing an API for plugins, that way people can have whatever they want. If you want to try and implement this feature for your project, the code for emitted is here and we log events here. It should be as simple as resetting that array/object.
@lmiller1990
So in your unit tests you never use BeforeAll / BeforeEach? That's what these are for, to do test suite setup.
beforeAll and beforeEach have their place - we use them for global stubs and configure in this library, for example. I personally don't use them for component related setup, because I don't think it's good practice. You are, of course, welcome to use them how you see fit.
I'm sorry if this is disappointing to you, but I don't see this feature as common enough to justify increasing the API surface, considering how much work making this all work with Vue 3 is going to be. I'd be happy to review code and assist if someone reading this comment feels strongly enough this feature. The code could be used like a plugin with existing projects.
Most helpful comment
I'd like this feature to be implemented, meanwhile I'm doing the following to reset/clear the emitted events:
component._emitted[event] = []