Skip to content

可重用性和组合

¥Reusability & Composition

大多:

¥Mostly:

  • global.mixins

  • global.directives

测试可组合项

¥Testing composables

使用组合 API 并创建可组合项时,你通常只想测试可组合项。让我们从一个简单的例子开始:

¥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 }
}

在这种情况下,你实际上并不需要 @vue/test-utils。这是相应的测试:

¥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)
})

对于更复杂的可组合项(使用 onMountedprovide/inject 处理等生命周期钩子),你可以创建一个简单的测试辅助程序组件。以下可组合项在 onMounted 钩子中获取用户数据。

¥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) {
    axios.get(`users/${id}`)
      .then(response => (user.value = response.data))
  }

  onMounted(() => fetchUser(userId))

  return { user }
}

要测试此可组合项,你可以在测试中创建一个简单的 TestComponentTestComponent 使用可组合项的方式应该与真实组件使用它的方式完全相同。

¥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
// 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 提供了一种通过 provideinject 将 props 传递给所有子组件的方法。测试此行为的最佳方法是测试整个树(父树+子树)。但有时这是不可能的,因为树太复杂,或者你只想测试单个可组合项。

¥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.

测试 provide

¥Testing provide

我们假设你要测试以下组件:

¥Let's assume the following component you want to test:

vue
<template>
  <div>
    <slot />
  </div>
</template>

<script setup>
provide('my-key', 'some-data')
</script>

在这种情况下,你可以渲染一个实际的子组件并测试 provide 的正确用法,也可以创建一个简单的测试辅助组件并将其传递到默认插槽中。

¥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: '<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')
})

如果你的组件不包含插槽,你可以使用 stub 并用测试助手替换子组件:

¥If your component does not contain a slot you can use a stub and replace a child component with your test helper:

vue
<template>
  <div>
    <SomeChild />
  </div>
</template>

<script setup>
import SomeChild from './SomeChild.vue'

provide('my-key', 'some-data')
</script>

和测试:

¥And the test:

typescript
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')
})

测试 inject

¥Testing inject

当你的组件使用 inject 并且你需要用 provide 传递数据时,那么你可以使用 global.provide 选项。

¥When your Component uses inject and you need to pass data with provide, then you can use the global.provide option.

vue
<template>
  <div>
    {{ value }}
  </div>
</template>

<script setup>
const value = inject('my-key')
</script>

单元测试可以简单地如下所示:

¥The unit test could simply look like:

typescript
test('renders correct data', () => {
  const wrapper = mount(MyComponent, {
    global: {
      provide: {
        'my-key': 'some-data'
      }
    }
  })

  expect(wrapper.text()).toBe('some-data')
})

结论

¥Conclusion

  • 测试没有组件和 @vue/test-utils 的简单可组合项

    ¥test simple composables without a component and @vue/test-utils

  • 创建一个测试辅助组件来测试更复杂的可组合项

    ¥create a test helper component to test more complex composables

  • 创建一个测试辅助组件来测试你的组件是否提供了正确的数据 provide

    ¥create a test helper component to test your component provides the correct data with provide

  • 使用 global.provide 将数据传递给使用 inject 的组件

    ¥use global.provide to pass data to your component which uses inject

Vue Test Utils 中文网 - 粤ICP备13048890号