From 2bf3739c5fc1add3715d30aff48dca026f3b0607 Mon Sep 17 00:00:00 2001 From: freakzlike Date: Mon, 11 Apr 2022 20:04:08 +0200 Subject: [PATCH 1/3] docs: Add testing composables section --- .../guide/advanced/reusability-composition.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/docs/guide/advanced/reusability-composition.md b/docs/guide/advanced/reusability-composition.md index 46be49e28..40bf991e9 100644 --- a/docs/guide/advanced/reusability-composition.md +++ b/docs/guide/advanced/reusability-composition.md @@ -5,3 +5,88 @@ Mostly: - `global.provide`. - `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: + +```typescript +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: + +```typescript +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. + +```typescript +export function useUser(userId) { + const user = ref() + + function fetchUser(id) { + fetch(`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. + +```typescript +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' }) +}) +``` From 6868898fafbd74791606f79f16570b89c7247ae0 Mon Sep 17 00:00:00 2001 From: freakzlike Date: Wed, 13 Apr 2022 19:46:06 +0200 Subject: [PATCH 2/3] docs: Add provide/inject section --- .../guide/advanced/reusability-composition.md | 125 +++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/docs/guide/advanced/reusability-composition.md b/docs/guide/advanced/reusability-composition.md index 40bf991e9..4ac6fcf38 100644 --- a/docs/guide/advanced/reusability-composition.md +++ b/docs/guide/advanced/reusability-composition.md @@ -2,7 +2,6 @@ Mostly: -- `global.provide`. - `global.mixins`. - `global.directives`. @@ -90,3 +89,127 @@ test('fetch user on mount', async () => { 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: +```vue + + + +``` + +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. + +```typescript +test('provides correct data', () => { + const TestComponent = defineComponent({ + template: '{{value}}', + 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`](./stubs-shallow-mount.md#stubbing-a-single-child-component) +and replace a child component with your test helper: + +```vue + + + +``` + +And the test: + +```typescript +test('provides correct data', () => { + const TestComponent = defineComponent({ + template: '{{value}}', + 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.provides` option. + +```vue + + + +``` + +The unit test could simply look like: + +```typescript +test('renders correct data', () => { + const wrapper = mount(MyComponent, { + global: { + provides: { + '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.provides` to pass data to your component which uses `inject` From c959d013c53c2d9dd9769a59cf972513add4a38c Mon Sep 17 00:00:00 2001 From: freakzlike Date: Sat, 16 Apr 2022 10:35:13 +0200 Subject: [PATCH 3/3] docs: Add mock of API request to testing composables section --- docs/guide/advanced/reusability-composition.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/guide/advanced/reusability-composition.md b/docs/guide/advanced/reusability-composition.md index 4ac6fcf38..c51a65546 100644 --- a/docs/guide/advanced/reusability-composition.md +++ b/docs/guide/advanced/reusability-composition.md @@ -44,7 +44,7 @@ export function useUser(userId) { const user = ref() function fetchUser(id) { - fetch(`users/${id}`) + axios.get(`users/${id}`) .then(response => (user.value = response.data)) } @@ -58,6 +58,9 @@ To test this composable, you can create a simple `TestComponent` within the test composable the exact same way how the real components would use it. ```typescript +// Mock API request +jest.spyOn(axios, 'get').mockResolvedValue({ data: { id: 1, name: 'User' } }) + test('fetch user on mount', async () => { const TestComponent = defineComponent({ props: {