英文:
Unit Testing VueRouter with the :to Attribute
问题
I have a simple test case (Vue 2, Vuetify 2, vue-router, jest, vue-test-utils) where a Vuetify v-btn component is clicked and goes to a new route:
<v-btn id="create-plan-btn" :to="{ 'name': 'newPlan', query: { dept: 216001 }}">Create plan</v-btn>
However, I am unable to figure out how to register a click method that captures the :to property's action.
describe('when "Create Plan button is clicked", () => {
const localVue = createLocalVue()
let vuetify
beforeEach(() => {
vuetify = new Vuetify()
})
const $route = { path: '/student/index', name: 'studentLanding' }
const mockRouter = {
to: jest.fn(),
push: jest.fn()
}
const wrapper = shallowMount(StudentLanding, {
localVue,
stubs: {'router-link': RouterLinkStub},
mocks: {
$route: $route,
$router: mockRouter
}
})
it('triggers Vue router call to the new plan page', async () => {
const button = wrapper.find('#create-plan-btn')
expect(button.exists()).toBe(true)
expect(mockRouter.push).toHaveBeenCalledTimes(0)
button.vm.$emit('click') // Vuetify buttons need "vm.$emit" to be triggered, not "trigger()" like a normal HTML button would
await wrapper.vm.$nextTick()
expect(mockRouter.push).toHaveBeenCalledTimes(1)
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'newPlan', query: { dept: 216001 }})
})
})
Note that changing push to to doesn't make any difference in the above code either.
The test does succeed, however, if I change my <v-btn> to use a @click callback instead:
// Test succeeds with this code
<v-btn id="create-plan-btn" @click="redirect">Create plan</v-btn>
redirect () {
this.$router.push({ name: 'newPlan', query: { dept: 216001 }});
}
Is there a way to modify this test that allows the :to prop to be captured on the <v-btn> click? I've tried to at least make sure the content of the :to prop is visible in the test, but I only ever get [object Object] sent back to me:
expect(button.attributes('to').toString()).toEqual({ name: 'newPlan', query: { dept: 216001 }})
Expected: {"name": "newPlan", "query": {"dept": 216001}}
Received: "[object Object]"
<details>
<summary>英文:</summary>
I have a simple test case (Vue 2, Vuetify 2, vue-router, jest, vue-test-utils) where a Vuetify v-btn component is clicked and goes to a new route
<v-btn id="create-plan-btn" :to="{ 'name': 'newPlan', query: { dept: 216001 }}">Create plan</v-btn>
However, I am unable to figure out how to register a click method that captures the **:to** property's action.
describe('when "Create Plan button is clicked', () => {
const localVue = createLocalVue()
let vuetify
beforeEach(() => {
vuetify = new Vuetify()
})
const $route = { path: '/student/index', name: 'studentLanding' }
const mockRouter = {
to: jest.fn(),
push: jest.fn()
}
const wrapper = shallowMount(StudentLanding, {
localVue,
stubs: {'router-link': RouterLinkStub},
mocks: {
$route: $route,
$router: mockRouter
}
})
it('triggers Vue router call to the new plan page', async () => {
const button = wrapper.find('#create-plan-btn')
expect(button.exists()).toBe(true)
expect(mockRouter.push).toHaveBeenCalledTimes(0)
button.vm.$emit('click') // Vuetify buttons need "vm.$emit" to be triggered, not "trigger()" like a normal HTML button would
await wrapper.vm.$nextTick()
expect(mockRouter.push).toHaveBeenCalledTimes(1)
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'newPlan', query: { dept: 216001 }})
})
})
> ● when "Create Plan button is clicked › triggers Vue router call to
> the new plan page
>
> expect(jest.fn()).toHaveBeenCalledTimes(expected)
>
> Expected number of calls: 1
> Received number of calls: 0
Note that changing **push** to **to** doesn't make any difference in the above code either.
The test **does** succeed, however, if I change my <v-btn> to use a @click callback instead:
// Test succeeds with this code
<v-btn id="create-plan-btn" @click="redirect">Create plan</v-btn>
redirect () {
this.$router.push({ name: 'newPlan', query: { dept: 216001 }});
}
Is there a way to modify this test that allows the :to prop to be captured on the <v-btn> click? I've tried to at least make sure the content of the :to prop is visible in the test, but I only ever get **[object Object]** sent back to me:
expect(button.attributes('to').toString()).toEqual({ name: 'newPlan', query: { dept: 216001 }})
> Expected: {"name": "newPlan", "query": {"dept": 216001}}
> Received: "[object Object]"
</details>
# 答案1
**得分**: 1
I'm fairly certain this has to do with your `emit` and not the fact that you're actually clicking it.
I'd suggest changing your test a bit:
```javascript
it('triggers Vue router call to the new plan page', async () => {
const button = wrapper.find('#create-plan-btn')
expect(button.exists()).toBeTruthy()
expect(mockRouter.push).toHaveBeenCalledTimes(0)
await button.trigger('click')
await wrapper.vm.$nextTick() // This could probably be omitted since we're awaiting the click above
expect(mockRouter.push).toHaveBeenCalledTimes(1)
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'newPlan', query: { dept: 216001 }})
})
Also, what would be the purpose of testing that the button attributes are what they are? Since you've written them there and they're not dynamic in any way, then they will be that. However, if you really do want to test it, I think the problem is the toString()
function, and you could instead try JSON stringifying it:
expect(JSON.stringify(button.attributes('to'))).toEqual({ name: 'newPlan', query: { dept: 216001 }})
Although it should also be possible to just do:
expect(button.attributes('to')).toStrictEqual({ name: 'newPlan', query: { dept: 216001 }})
I don't really know what you mean with your comment that Vuetify buttons would need to use $emit
, I've always managed to test Vuetify buttons this way.
I'd also suggest one simple change, instead of using ID's for testing, create an attribute data-testid="router-button"
that you name and find the component with:
const button = wrapper.find('[data-testid="router-button"]')
This makes it clear that it's used for testing and nothing else.
英文:
I'm fairly certain this has to do with your emit
and not the fact that you're actually clicking it.
I'd suggest changing your test a bit:
it('triggers Vue router call to the new plan page', async () => {
const button = wrapper.find('#create-plan-btn')
expect(button.exists()).toBeTruthy()
expect(mockRouter.push).toHaveBeenCalledTimes(0)
await button.trigger('click')
await wrapper.vm.$nextTick() // This could probably be omitted since we're awaiting the click above
expect(mockRouter.push).toHaveBeenCalledTimes(1)
expect(mockRouter.push).toHaveBeenCalledWith({ name: 'newPlan', query: { dept: 216001 }})
})
Also, what would be the purpose of testing that the button attributes are what they are? Since you've written them there and they're not dynamic in any way, then they will be that. However, if you really do want to test it, I think the problem is the toString()
function, and you could instead try JSON stringifying it:
expect(JSON.stringify(button.attributes('to'))).toEqual({ name: 'newPlan', query: { dept: 216001 }})
Although it should also be possible to just do:
expect(button.attributes('to')).toStrictEqual({ name: 'newPlan', query: { dept: 216001 }})
I don't really know what you mean with your comment that Vuetify buttons would need to use $emit
, I've always managed to test Vuetify buttons this way.
I'd also suggest one simple change, instead of using ID's for testing, create an attribute data-testid="router-button"
that you name and find the component with:
const button = wrapper.find('[data-testid="router-button"]')
This makes it clear that it's used for testing and nothing else.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论