带有 vue-test-utils 和 Jest 的 Vue.js 单元测试用例失败

Posted

技术标签:

【中文标题】带有 vue-test-utils 和 Jest 的 Vue.js 单元测试用例失败【英文标题】:Vue.js unit test cases with vue-test-utils and Jest failing 【发布时间】:2019-01-27 19:48:57 【问题描述】:

使用 vue-cli 3,我开始学习用 Jest 进行单元测试

我为我的初始标题组件编写了我的第一个规范

import  shallowMount  from "@vue/test-utils";
import Heading from "@/components/Home/Heading.vue";

describe("Heading.vue", () => 
  it("should contains default heading", () => 
    // when
    const wrapper = shallowMount(Heading);
    // then
    const heading = wrapper.find('h1');
    expect(heading.text()).toContain('In the heart of Charentes...')
  );
);

但我在 y 组件 (??,) 中遇到语法错误

无效或意外的令牌

vue-cli-service test:unit

 FAIL  tests/unit/Heading.spec.js (16.815s)
  Heading.vue
    ✕ should contains default heading (215ms)

  ● Heading.vue › should contains default heading

    /Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/assets/images/hero.jpeg:1    ("Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest)����
                                                                                             ^

    SyntaxError: Invalid or unexpected token

      124 | .primaryInversed 
      125 |   background-color: white !important;
    > 126 |   border-color: #464898 !important;
          |                                                                                                ^
      127 |   color: #464898 !important;
      128 |   .icon 
      129 |     color: #464898 !important;

      at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)

我不明白为什么......(我正在关注来自octo talks的小嘟嘟声

这是我的组件

<template>
      <section id='heading'>
        <v-parallax :src="require('@/assets/images/hero.jpeg')">
          <v-layout column align-center justify-center>
            <img src="@/assets/images/logo.png"  >
            <h1 class="mb-2 display-1 text-xs-center"> $t('lang.views.home.heading.header1') </h1>
            <h2 class="mb-2 display-1 text-xs-center">  $t('lang.views.home.heading.header2') </h2>
            <div class="subheading mb-3 text-xs-center"> $t('lang.views.home.heading.subheading') </div>
            <v-btn round v-if="!listening" @click="startAudioPlayer()" class="primary" large href="#"> $t('lang.views.home.heading.btn__listen') 
              <v-icon right>play_arrow</v-icon>
            </v-btn>
            <v-btn round v-else @click="stopAudioPlayer()" class="primaryInversed" large href="#"> $t('lang.views.home.heading.btn__stop') 
              <v-icon right>pause_circle_outline</v-icon>
            </v-btn>
            <audioplayer id="audioplayer" v-if="audioPlayer" :autoPlay="shouldPlay" :file="audioFile" :canPlay="audioReady" :ended="audioFinish"></audioplayer>
         </v-layout>
        </v-parallax>

        <div id='content'>
          <v-layout row wrap class='my-5' >
            <v-flex xs12 sm4>
              <v-card class='elevation-0'>
                <v-card-text class="text-xs-center">
                  <v-icon x-large class="">group</v-icon>
                </v-card-text>
                <v-card-title primary-title class="layout justify-center">
                  <div class="headline text-xs-center" v-html="$t('lang.views.home.heading.card1__title')"></div>
                </v-card-title>
                <v-card-text v-html="$t('lang.views.home.heading.card1__content')"></v-card-text>
              </v-card>
            </v-flex>

            <v-flex xs12 sm4>
              <v-card class='elevation-0'>
                <v-card-text class='text-xs-center'>
                  <v-icon x-large class="">audiotrack</v-icon>
                </v-card-text>
                <v-card-title primary-title class="layout justify-center">
                  <div class="headline" v-html="$t('lang.views.home.heading.card2__title')"></div>
                </v-card-title>
                <v-card-text v-html="$t('lang.views.home.heading.card2__content')"></v-card-text>
              </v-card>
            </v-flex>

            <v-flex xs12 sm4>
              <v-card class='elevation-0'>
                <v-card-text class='text-xs-center'>
                   <v-icon x-large class="">tap_and_play</v-icon>
                </v-card-text>
                <v-card-title primary-title class="layout justify-center">
                  <div class="headline text-xs-center" v-html="$t('lang.views.home.heading.card3__title')"></div>
                </v-card-title>
                <v-card-text v-html="$t('lang.views.home.heading.card3__content')"></v-card-text>
              </v-card>
            </v-flex>
          </v-layout>
        </div>
      </section>
</template>

<script>
import AudioPlayer from "@/components/Home/AudioPlayer.vue";
// import  mapGetters  from 'vuex'
export default 
  name: "Heading",
  data() 
    return 
      listening: false,
      file: "ultimo_desejo",
      audioPlayer: false,
      audioPlayerShouldPlay: true // autoplay
    ;
  ,
  components: 
    audioplayer: AudioPlayer
  ,
  computed: 
    // ...mapGetters(['webpSupport']),
    shouldPlay() 
      return this.audioPlayerShouldPlay;
    ,
    audioFile() 
      return require("@/assets/audio/" + this.file + ".mp3");
    
  ,
  methods: 
    audioReady() 
      // console.log('You see this means audio can start.')
    ,
    audioFinish() 
      // console.log('You see this means audio finish.')
      this.listening = false;
      this.audioPlayer = false;
    ,
    showAudioPlayer() 
      this.audioPlayer = true;
    ,
    startAudioPlayer() 
      this.listening = true;
      this.audioPlayer = true;
    ,
    stopAudioPlayer() 
      this.listening = false;
      this.audioPlayer = false;
    
  ,
  mounted() 
    // console.log('HEADING webp format support: ', this.webpSupport)
  ,
  beforeMount() 
    this.playerAutoPlay = this.autoPlay; // save props data to itself's data and deal with it
  
;
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
#audioplayer 
  display: none; // hide the autoplayer !

.btn__content .icon 
  color: white !important;

.primaryInversed 
  background-color: white !important;
  border-color: #464898 !important;
  color: #464898 !important;
  .icon 
    color: #464898 !important;
  

</style>

更新

这是完整的 console.log 错误输出

    > vue-cli-service test:unit

     FAIL  tests/unit/Heading.spec.js (8.158s)
      Heading.vue
        ✕ should contains default heading (226ms)

      ● Heading.vue › should contains default heading

        /Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/assets/images/hero.jpeg:1
        ("Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest)����
                                                                                                 ^

        SyntaxError: Invalid or unexpected token

          124 | .primaryInversed 
          125 |   background-color: white !important;
        > 126 |   border-color: #464898 !important;
              |                                                                                                ^
          127 |   color: #464898 !important;
          128 |   .icon 
          129 |     color: #464898 !important;

          at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:403:17)
          at Proxy.render (src/components/Home/Heading.vue:126:186)
          at VueComponent.Vue._render (node_modules/vue/dist/vue.runtime.common.js:4542:22)
          at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.js:2786:21)
          at Watcher.get (node_modules/vue/dist/vue.runtime.common.js:3140:25)
          at new Watcher (node_modules/vue/dist/vue.runtime.common.js:3129:12)

      console.error node_modules/vue/dist/vue.runtime.common.js:589
        [Vue warn]: Error in config.errorHandler: "SyntaxError: Invalid or unexpected token"

      console.error node_modules/vue/dist/vue.runtime.common.js:1739
        /Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/assets/images/hero.jpeg:1
        ("Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest)����
                                                                                                 ^

        SyntaxError: Invalid or unexpected token
            at ScriptTransformer._transformAndBuildScript (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/script_transformer.js:403:17)
            at ScriptTransformer.transform (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/script_transformer.js:448:19)
            at Runtime._execModule (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:640:53)
            at Runtime.requireModule (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:376:14)
            at Runtime.requireModuleOrMock (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:463:19)
            at Proxy.render (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/components/Home/Heading.vue:126:186)
            at VueComponent.Vue._render (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:4542:22)
            at VueComponent.updateComponent (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:2786:21)
            at Watcher.get (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:3140:25)
            at new Watcher (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:3129:12)

      console.error node_modules/vue/dist/vue.runtime.common.js:589
        [Vue warn]: Error in render: "SyntaxError: Invalid or unexpected token"

        found in

        ---> <Heading>
               <Root>

      console.error node_modules/vue/dist/vue.runtime.common.js:1739
        /Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/assets/images/hero.jpeg:1
        ("Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest)����
                                                                                                 ^

        SyntaxError: Invalid or unexpected token
            at ScriptTransformer._transformAndBuildScript (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/script_transformer.js:403:17)
            at ScriptTransformer.transform (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/script_transformer.js:448:19)
            at Runtime._execModule (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:640:53)
            at Runtime.requireModule (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:376:14)
            at Runtime.requireModuleOrMock (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/jest-runtime/build/index.js:463:19)
            at Proxy.render (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/src/components/Home/Heading.vue:126:186)
            at VueComponent.Vue._render (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:4542:22)
            at VueComponent.updateComponent (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:2786:21)
            at Watcher.get (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:3140:25)
            at new Watcher (/Users/yves/Developments/WIP/VUE.JS-cli-3/3-chocha-home-content/chocha/node_modules/vue/dist/vue.runtime.common.js:3129:12)

【问题讨论】:

【参考方案1】:

在这个特定的案例中,错误来自 hero.jpeg 图像文件。我应该将 jpeg 扩展名添加到 package.json 中的“jest”“transform”块中,该块目前仅转换 jpg 文件。

"transform": 
  "^.+\\.vue$": "vue-jest",
  ".+\\.(css|styl|less|sass|scss|png|jpg|jpeg|mp3|ttf|woff|woff2)$": "jest-transform-stub",
  "^.+\\.jsx?$": "babel-jest",
  "^.+\\.js?$": "babel-jest"
,

【讨论】:

【参考方案2】:

另一种可能的解决方案是将其添加到 package.json 文件的“jest”对象的“moduleNameMapper”对象中:

"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/mocks/fileMock.js",
"\\.(css|scss)$": "<rootDir>/test/mocks/styleMock.js"

fileMock.js 的内容:

module.exports = 'test-file-stub';

styleMock.js 的内容:

module.exports = ;

第一个模拟所有文件资源(图像和媒体),第二个模拟样式文件。

【讨论】:

以上是关于带有 vue-test-utils 和 Jest 的 Vue.js 单元测试用例失败的主要内容,如果未能解决你的问题,请参考以下文章

无法在基于 jest 和 vue-test-utils 的测试项目中使用 vue.js 的 ES6 模块

Vue-test-utils | Jest:如何处理依赖关系?

使用 `vue-test-utils` 和 `jest` 使用 `Created` 钩子进行测试

如何使用 Vue-test-utils 和 Jest 测试 Vuex 突变

如何在单元测试期间使用 vue-test-utils 和 jest 模拟 mixin?

使用 vue-test-utils / jest 触发 Quasar QBtn 点击