开玩笑+ Vue Test Utils:无法读取未定义的属性(读取“组件”)

发布于 2025-01-13 04:55:37 字数 11898 浏览 3 评论 0原文

我对测试 UI 元素还很陌生,在这里我很茫然。我有一个组件 ProjectDashboardView,它在挂载时从 API 获取一些信息,并使用获取的信息呈现一系列 ProjectDashboardCard 组件。我正在模拟测试中的 API 调用,使用 flush-promises 等待所有承诺在断言之前得到解决。但是,我收到此错误:

  ● projectDashboardView › displays ProjectDashboardCards corresponding to all the repositories fetched

    TypeError: Cannot read properties of undefined (reading 'component')

      14 | describe('projectDashboardView', () => {
      15 |     it('displays ProjectDashboardCards corresponding to all the repositories fetched', async () => {
    > 16 |         const wrapper = await mount(ProjectDashboardView, {
         |                               ^
      17 |             router,
      18 |             localVue,
      19 |             mocks: {

      at remapInternalIcon (node_modules/vuetify/dist/webpack:/Vuetify/src/util/helpers.ts:233:39)
      at VueComponent.getIcon (node_modules/vuetify/dist/webpack:/Vuetify/src/components/VIcon/VIcon.ts:72:31)
      at Proxy.render (node_modules/vuetify/dist/webpack:/Vuetify/src/components/VIcon/VIcon.ts:217:23)
      at VueComponent.Vue._render (node_modules/vue/dist/vue.runtime.common.dev.js:3559:22)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:21)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:14057:21)

这是测试和组件的代码:

ProjectDashboardView.spec.js

import { mount, createLocalVue } from '@vue/test-utils'
import { SERVICES } from '@/utils/common'
import VueRouter from 'vue-router'
import router from '@/router'
import flushPromises from 'flush-promises'
import ProjectDashboardView from '@/components/ProjectDashboard/ProjectDashboardView.vue'
import ProjectDashboardCard from '@/components/ProjectDashboard/ProjectDashboardCard.vue'

jest.mock('@/services/finderService')

const localVue = createLocalVue()
localVue.use(VueRouter)

describe('projectDashboardView', () => {
    it('displays ProjectDashboardCards corresponding to all the repositories fetched', async () => {
        const wrapper = mount(ProjectDashboardView, {
            router,
            localVue,
            mocks: {
                $store: {
                    state: {
                        user: {
                            name: '',
                            // Array of IDs
                            repositories: [1, 2],
                            // Services the user is using to test code quality in their projects
                            services: SERVICES,
                        },
                    },
                },
            },
        })

        await flushPromises()

        const children = wrapper.findAllComponents(ProjectDashboardCard)
        expect(children.length).toBeGreaterThan(0)
    })
})

ProjectDashboardView.js

<template>
    <v-container class="grid">
        <project-dashboard-card
            v-for="(value, name) in latestReports"
            :key="name"
            :repository="value"
        />
        <the-add-project-button />
    </v-container>
</template>

<script>
import finderService from '@/services/finderService'
import ProjectDashboardCard from '@/components/ProjectDashboard/ProjectDashboardCard'
import TheAddProjectButton from '@/components/ProjectDashboard/TheAddProjectButton'
import { mapState } from 'vuex'

export default {
    name: 'ProjectDashboardView',

    components: {
        ProjectDashboardCard,
        TheAddProjectButton,
    },

    data() {
        return {
            latestReports: [],
        }
    },

    computed: {
        ...mapState(['user']),
    },

    mounted() {
        this.fetchLatestReports()
    },

    methods: {
        fetchLatestReports() {
            const reportTuples = []

            for (const repo of this.user.repositories) {
                for (const service of this.user.services) {
                    reportTuples.push([service, repo])
                }
            }

            const allReports = Promise.all(
                reportTuples.map(t => finderService.find(t[0], t[1], true))
            )

            const latestReports = {}

            allReports
                .then(responses => {
                    const filteredResponses = responses.filter(
                        response => response.data.length > 0
                    )
                    filteredResponses.forEach(response => {
                        if (!latestReports[`repo${response.repoID}`]) {
                            latestReports[`repo${response.repoID}`] = {}
                        }
                        if (!latestReports[`repo${response.repoID}`].repoName) {
                            latestReports[`repo${response.repoID}`].repoName =
                                response.data[0].project_name
                        }
                        if (!latestReports[`repo${response.repoID}`].services) {
                            latestReports[`repo${response.repoID}`].services =
                                {}
                        }
                        latestReports[`repo${response.repoID}`].services[
                            response.service
                        ] = response.data[0]
                    })

                    this.latestReports = latestReports
                })
                .catch(err => console.log(err))
        },
    },
}
</script>

以防万一,这里也是 API 调用模拟的代码:

import sonarqubeReport from './fixtures/find.sonarqube.1.json'
import gitleaksReport from './fixtures/find.gitleaks.1.json'
import dependencycheckReport from './fixtures/find.dependencycheck.1.json'
import hadolintReport from './fixtures/find.hadolint.1.json'
import trivyReport from './fixtures/find.trivy.1.json'

const mockGetResponse = service => {
    switch (service) {
        case 'sonarqube':
            return sonarqubeReport
        case 'gitleaks':
            return gitleaksReport
        case 'dependencycheck':
            return dependencycheckReport
        case 'hadolint':
            return hadolintReport
        case 'trivy':
            return trivyReport
        default:
            break
    }
}

const finderService = {
    find: service => Promise.resolve({ body: mockGetResponse(service) }),
}

export default finderService

I'm pretty new to testing UI elements and I'm at a loss here. I have a component, ProjectDashboardView, that fetches some information from an API on mount and renders a series of ProjectDashboardCard components with that fetched information. I'm mocking the API call on the tests, using flush-promises to wait for all promises to be resolved before assertion. However, I'm getting this error:

  ● projectDashboardView › displays ProjectDashboardCards corresponding to all the repositories fetched

    TypeError: Cannot read properties of undefined (reading 'component')

      14 | describe('projectDashboardView', () => {
      15 |     it('displays ProjectDashboardCards corresponding to all the repositories fetched', async () => {
    > 16 |         const wrapper = await mount(ProjectDashboardView, {
         |                               ^
      17 |             router,
      18 |             localVue,
      19 |             mocks: {

      at remapInternalIcon (node_modules/vuetify/dist/webpack:/Vuetify/src/util/helpers.ts:233:39)
      at VueComponent.getIcon (node_modules/vuetify/dist/webpack:/Vuetify/src/components/VIcon/VIcon.ts:72:31)
      at Proxy.render (node_modules/vuetify/dist/webpack:/Vuetify/src/components/VIcon/VIcon.ts:217:23)
      at VueComponent.Vue._render (node_modules/vue/dist/vue.runtime.common.dev.js:3559:22)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:21)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at createChildren (node_modules/vue/dist/vue.runtime.common.dev.js:6077:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5978:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3131:13)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:6002:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5949:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6499:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3948:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4069:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4481:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4470:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4076:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8436:10)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:14057:21)

Here's the code for both the test and the component:

ProjectDashboardView.spec.js

import { mount, createLocalVue } from '@vue/test-utils'
import { SERVICES } from '@/utils/common'
import VueRouter from 'vue-router'
import router from '@/router'
import flushPromises from 'flush-promises'
import ProjectDashboardView from '@/components/ProjectDashboard/ProjectDashboardView.vue'
import ProjectDashboardCard from '@/components/ProjectDashboard/ProjectDashboardCard.vue'

jest.mock('@/services/finderService')

const localVue = createLocalVue()
localVue.use(VueRouter)

describe('projectDashboardView', () => {
    it('displays ProjectDashboardCards corresponding to all the repositories fetched', async () => {
        const wrapper = mount(ProjectDashboardView, {
            router,
            localVue,
            mocks: {
                $store: {
                    state: {
                        user: {
                            name: '',
                            // Array of IDs
                            repositories: [1, 2],
                            // Services the user is using to test code quality in their projects
                            services: SERVICES,
                        },
                    },
                },
            },
        })

        await flushPromises()

        const children = wrapper.findAllComponents(ProjectDashboardCard)
        expect(children.length).toBeGreaterThan(0)
    })
})

ProjectDashboardView.js

<template>
    <v-container class="grid">
        <project-dashboard-card
            v-for="(value, name) in latestReports"
            :key="name"
            :repository="value"
        />
        <the-add-project-button />
    </v-container>
</template>

<script>
import finderService from '@/services/finderService'
import ProjectDashboardCard from '@/components/ProjectDashboard/ProjectDashboardCard'
import TheAddProjectButton from '@/components/ProjectDashboard/TheAddProjectButton'
import { mapState } from 'vuex'

export default {
    name: 'ProjectDashboardView',

    components: {
        ProjectDashboardCard,
        TheAddProjectButton,
    },

    data() {
        return {
            latestReports: [],
        }
    },

    computed: {
        ...mapState(['user']),
    },

    mounted() {
        this.fetchLatestReports()
    },

    methods: {
        fetchLatestReports() {
            const reportTuples = []

            for (const repo of this.user.repositories) {
                for (const service of this.user.services) {
                    reportTuples.push([service, repo])
                }
            }

            const allReports = Promise.all(
                reportTuples.map(t => finderService.find(t[0], t[1], true))
            )

            const latestReports = {}

            allReports
                .then(responses => {
                    const filteredResponses = responses.filter(
                        response => response.data.length > 0
                    )
                    filteredResponses.forEach(response => {
                        if (!latestReports[`repo${response.repoID}`]) {
                            latestReports[`repo${response.repoID}`] = {}
                        }
                        if (!latestReports[`repo${response.repoID}`].repoName) {
                            latestReports[`repo${response.repoID}`].repoName =
                                response.data[0].project_name
                        }
                        if (!latestReports[`repo${response.repoID}`].services) {
                            latestReports[`repo${response.repoID}`].services =
                                {}
                        }
                        latestReports[`repo${response.repoID}`].services[
                            response.service
                        ] = response.data[0]
                    })

                    this.latestReports = latestReports
                })
                .catch(err => console.log(err))
        },
    },
}
</script>

Just in case, here's the code for the API call mock too:

import sonarqubeReport from './fixtures/find.sonarqube.1.json'
import gitleaksReport from './fixtures/find.gitleaks.1.json'
import dependencycheckReport from './fixtures/find.dependencycheck.1.json'
import hadolintReport from './fixtures/find.hadolint.1.json'
import trivyReport from './fixtures/find.trivy.1.json'

const mockGetResponse = service => {
    switch (service) {
        case 'sonarqube':
            return sonarqubeReport
        case 'gitleaks':
            return gitleaksReport
        case 'dependencycheck':
            return dependencycheckReport
        case 'hadolint':
            return hadolintReport
        case 'trivy':
            return trivyReport
        default:
            break
    }
}

const finderService = {
    find: service => Promise.resolve({ body: mockGetResponse(service) }),
}

export default finderService

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文