Vue Test Utils - 无法正确 mount/shallow 挂载组件,包装器未定义

Vue Test Utils - Unable to correctly mount/shallow mount component, wrapper undefined

我已经尝试了几乎所有我能想到的方法,但我无法正确 mount/shallow 挂载我的 vue 组件以进行正确测试。每次我 console.log 包装器时,我都会打印出以下内容:

VueWrapper {
  isFunctionalComponent: undefined,
  _emitted: [Object: null prototype] {},
  _emittedByOrder: []
}

这个问题类似于这里问的这个问题:

我正在使用 Vuetify、Vuex 和 Vue 路由器。我的test.spec.ts如下:

import { shallowMount, createLocalVue, mount } from "@vue/test-utils"
import Vuex from "vuex"
import Vuetify from "vuetify"
import VueRouter from "vue-router"
import TheExamAnswer from "@/components/common/TheExamAnswer.vue"

describe("TheExamAnswer.vue", () => {
  const localVue = createLocalVue()
  let getters: any
  let store: any
  let vuetify: any
  let router: any

  beforeEach(() => {
    localVue.use(Vuex)
    localVue.use(Vuetify)
    localVue.use(VueRouter)
    getters = {
      getExam: () => true,
    }

    store = new Vuex.Store({
      modules: {
        // Need to add FlightPlanning for name spacing
        FlightPlanning: {
          namespaced: true,
          getters,
        },
      },
    })

    vuetify = new Vuetify()

    router = new VueRouter()
  })

  it("Renders the element if the exam has been submitted", () => {
    const wrapper = mount(TheExamAnswer, { localVue, store, router })
    console.log("This is the HTML", wrapper.html())
    expect(wrapper.text()).toContain("Show Answer")
  })
})

我的视图组件很简单,代码如下:

<template>
  <div v-if="submitted" class="div">
    <v-btn @click="answerHidden = !answerHidden" class="mb-10"
      >Show Answer</v-btn
    >
    <div v-if="!answerHidden">
      <slot name="questionAnswer"></slot>
    </div>
  </div>
</template>

<script>
export default {
  data: () => {
    return {
      answerHidden: true,
    }
  },
  computed: {
    submitted() {
      const exam = this.$store.getters["FlightPlanning/getExam"]
      return exam.submitted
    },
  },
}
</script>

<style></style>

更新: 我已经从下面的答案中添加了建议,但是现在我收到了以下消息。

 TheExamAnswer.vue
    ✕ Renders the element if the exam has been submitted (49ms)

  ● TheExamAnswer.vue › Renders the element if the exam has been submitted

    expect(received).toContain(expected) // indexOf

    Expected substring: "Show Answer"
    Received string:    ""

      38 |     const wrapper = mount(TheExamAnswer, { localVue, store, router })
      39 |     console.log("This is the HTML", wrapper.html())
    > 40 |     expect(wrapper.text()).toContain("Show Answer")
         |                            ^
      41 |   })
      42 | })
      43 | 

      at Object.it (tests/unit/test.spec.ts:40:28)

  console.error node_modules/vuetify/dist/vuetify.js:43612
    [Vuetify] Multiple instances of Vue detected
    See https://github.com/vuetifyjs/vuetify/issues/4068
    
    If you're seeing "$attrs is readonly", it's caused by this

  console.log tests/unit/test.spec.ts:39
    This is the HTML 

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total

如您所见,HTML 是空白的,因此我推测这也是测试失败的原因,因为接收到的字符串是“”。

解 -

我想通了。由于没有正确查看计算 属性 的逻辑,该错误代表我。

在我的测试中我有:

 getters = {
          getExam: () => true,
        }

在我的组件中我有:

computed: {
    submitted() {
      const exam = this.$store.getters["FlightPlanning/getExam"]
      return exam.submitted
    },

如果您查看计算 属性 的逻辑,它将从 getter 中获取 returned 并将其分配给考试变量。最初我是 returning true,因为这就是我想要 return 的 submitted() 函数,这意味着当我调用 exam.submitted 时,我是在一个布尔值上调用它,这显然给了我“不明确的”。解决方案是 return 正是计算的 属性 旨在处理的对象,即 {submitted:true}

因此最终测试看起来像这样 return 有效 HTML。

import { shallowMount, createLocalVue, mount } from "@vue/test-utils"
import Vuex from "vuex"
import Vuetify from "vuetify"
import VueRouter from "vue-router"
import TheExamAnswer from "@/components/common/TheExamAnswer.vue"

const localVue = createLocalVue()

localVue.use(Vuex)
localVue.use(Vuetify)
localVue.use(VueRouter)

describe("test.vue", () => {
  let getters: any
  let store: any
  let vuetify: any
  let router: any

  beforeEach(() => {
    getters = {
      getExam: () => {
        return { submitted: true }
      },
    }

    store = new Vuex.Store({
      modules: {
        // Need to add FlightPlanning for name spacing
        FlightPlanning: {
          namespaced: true,
          getters,
        },
      },
    })

    vuetify = new Vuetify()

    router = new VueRouter()
  })

  it("Renders the element if the exam has been submitted", () => {
    const wrapper = mount(TheExamAnswer, { localVue, vuetify, store, router })
    console.log("This is the HTML", wrapper.html())
  })
})

这给了我以下结果:

  console.log tests/unit/test.spec.ts:44
    This is the HTML <div><button type="button" class="mb-10 v-btn v-btn--contained theme--light v-size--default"><span class="v-btn__content">Show Answer</span></button>
      <!---->
    </div>

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.235s
Ran all test suites.

console.log 带有奇怪的包装器或元素输入是正常行为。我 运行 对您的组件进行了测试,一切正常。

it("Renders the element if the exam has been submitted", () => {
  const wrapper = mount(TheExamAnswer, { localVue, store, router });
  expect(wrapper.text()).toContain("Show Answer");
});

如果你想 console.log html 在你的包装器中:

console.log(wrapper.html())

更新: 原因,为什么 wrapper.html() return 空字符串在根组件上是 v-if="submitted"。计算出来的属性returnundefined,因为getterreturntrue,所以true.submittedreturnundefined

Getter 在 test.spec.ts:

getters = {
  getExam: () => {
    return { submitted: true };
  }
};