Reusability & Composition
Mostly:
global.mixins
.global.directives
.
Testing composables
When working with the composition API and creating composables, you often want to test only the composable. Let's start with a simple example:
export function useCounter() {
const counter = ref(0)
function increase() {
counter.value += 1
}
return { counter, increase }
}
In this case, you don't actually need @vue/test-utils
. Here is the corresponding test:
test('increase counter on call', () => {
const { counter, increase } = useCounter()
expect(counter.value).toBe(0)
increase()
expect(counter.value).toBe(1)
})
For more complex composables, which use lifecycle hooks like onMounted
or provide
/inject
handling, you can create a simple test helper component. The following composable fetches the user data within the onMounted
hook.
export function useUser(userId) {
const user = ref()
function fetchUser(id) {
axios.get(`users/${id}`)
.then(response => (user.value = response.data))
}
onMounted(() => fetchUser(userId))
return { user }
}
To test this composable, you can create a simple TestComponent
within the tests. The TestComponent
should use the composable the exact same way how the real components would use it.
// Mock API request
jest.spyOn(axios, 'get').mockResolvedValue({ data: { id: 1, name: 'User' } })
test('fetch user on mount', async () => {
const TestComponent = defineComponent({
props: {
// Define props, to test the composable with different input arguments
userId: {
type: Number,
required: true
}
},
setup (props) {
return {
// Call the composable and expose all return values into our
// component instance so we can access them with wrapper.vm
...useUser(props.userId)
}
}
})
const wrapper = mount(TestComponent, {
props: {
userId: 1
}
})
expect(wrapper.vm.user).toBeUndefined()
await flushPromises()
expect(wrapper.vm.user).toEqual({ id: 1, name: 'User' })
})
Provide / inject
Vue offers a way to pass props to all child components with provide
and inject
. The best way to test this behavior is to test the entire tree (parent + children). But sometimes this is not possible, because the tree is too complex, or you only want to test a single composable.
Testing provide
Let's assume the following component you want to test:
<template>
<div>
<slot />
</div>
</template>
<script setup>
provide('my-key', 'some-data')
</script>
In this case you could either render an actual child component and test the correct usage of provide
or you can create a simple test helper component and pass it into the default slot.
test('provides correct data', () => {
const TestComponent = defineComponent({
template: '<span id="provide-test">{{value}}</span>',
setup () {
const value = inject('my-key')
return { value }
}
})
const wrapper = mount(ParentComponent, {
slots: {
default: () => h(TestComponent)
}
})
expect(wrapper.find('#provide-test').text()).toBe('some-data')
})
If your component does not contain a slot you can use a stub
and replace a child component with your test helper:
<template>
<div>
<SomeChild />
</div>
</template>
<script setup>
import SomeChild from './SomeChild.vue'
provide('my-key', 'some-data')
</script>
And the test:
test('provides correct data', () => {
const TestComponent = defineComponent({
template: '<span id="provide-test">{{value}}</span>',
setup () {
const value = inject('my-key')
return { value }
}
})
const wrapper = mount(ParentComponent, {
global: {
stubs: {
SomeChild: TestComponent
}
}
})
expect(wrapper.find('#provide-test').text()).toBe('some-data')
})
Testing inject
When your Component uses inject
and you need to pass data with provide
, then you can use the global.provide
option.
<template>
<div>
{{ value }}
</div>
</template>
<script setup>
const value = inject('my-key')
</script>
The unit test could simply look like:
test('renders correct data', () => {
const wrapper = mount(MyComponent, {
global: {
provide: {
'my-key': 'some-data'
}
}
})
expect(wrapper.text()).toBe('some-data')
})
Conclusion
- test simple composables without a component and
@vue/test-utils
- create a test helper component to test more complex composables
- create a test helper component to test your component provides the correct data with
provide
- use
global.provide
to pass data to your component which usesinject