Vue单元测试失败,因为组件方法调用了此。$ route.query-TypeError:无法读取未定义的属性“ query”

时间:2019-09-10 13:31:43

标签: vue.js testing mocha vue-router chai

长期使用StackOverflow的智能用户,但我第一次发布问题。多么里程碑!

我正在为大型Vue应用程序编写单元测试,我的组件之一使用了一种引用$ route的方法,以确定是否在使用查询参数/在参数中传递查询参数。调用this。$ route.query.article_id的方法效果很好,但是由于我正在测试中,因此测试无法识别出此。$ route.query

如文档中所述,我试图在使用shallowMount挂载我的localVue时模拟$ route对象,但是它不起作用,并且我继续遇到相同的错误。

这是我的组件:

<template>
  <b-container fluid>
    <div class="content-page-header"></div>
    <b-row>
      <b-col cols="3" class="outer-columns text-center" style="color:grey">
        <font-awesome-icon
          :icon="['fas', 'newspaper']"
          class="fa-9x content-page-photo mb-3 circle-icon"
        />
        <br />
        <br />Get practical tips and helpful
        <br />advice in clear articles written
        <br />by our staff's experts.
      </b-col>
      <b-col cols="6" v-if="articlesExist">
        <h1 class="header-text">
          <b>Articles</b>
        </h1>
        <div v-if="!selectedArticle">
          <div v-for="article in articles">
            <article-card :article="article" @clicked="onClickRead" />
            <br />
          </div>
        </div>
        <div v-else>
          <router-link to="articles" v-on:click.native="setSelectedArticle(null)">
            <font-awesome-icon icon="chevron-circle-left" />&nbsp
            <b>Back to All Articles</b>
          </router-link>
          <article-header :article="selectedArticle" />
          <br />
          <span v-html="selectedArticle.text"></span>
        </div>
      </b-col>
      <b-col cols="6" v-else>
        <h1 class="header-text">
          <b>Articles</b>
        </h1>
        <div class="text-center">Stay tuned for more Articles</div>
      </b-col>
      <b-col class="outer-columns">
        <b class="text-color" style="font-size:14pt">Saved Articles</b>
        <div v-for="article in userArticles">
          <router-link
            :to="{path:'articles', query: {article_id: article.article.id}}"
            v-on:click.native="setSelectedArticle(article.article)"
          >
            <user-article :article="article.article" />
          </router-link>
          <br />
        </div>
      </b-col>
    </b-row>
  </b-container>
</template>

<script>
import ArticleCard from "./ArticleCard";
import UserArticle from "./UserArticle";
import ArticleHeader from "./ArticleHeader";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
  faNewspaper,
  faChevronCircleLeft
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";

library.add(faNewspaper, faChevronCircleLeft);

export default {
  name: "Articles",

  props: [],

  components: {
    ArticleCard,
    ArticleHeader,
    UserArticle,
    library,
    FontAwesomeIcon,
    faNewspaper,
    faChevronCircleLeft
  },

  mixins: [],

  data() {
    return {
      selectedArticle: null
    };
  },

  computed: {
    articles() {
      return this.$store.getters.articles.filter(article => article.text);
    },
    articlesExist() {
      return Array.isArray(this.articles) && this.articles.length;
    },
    userArticles() {
      return this.$store.getters.userArticles;
    },
    articleParam() {
      return parseInt(this.$route.query.article_id);
    }
  },

  methods: {
    setSelectedArticle(article) {
      this.selectedArticle = article;
    },
    onClickRead(article) {
      this.selectedArticle = article;
    }
  },

  mounted() {
    if (this.articleParam) {
      this.setSelectedArticle(
        this.articles.filter(article => article.id === this.articleParam)[0]
      );
    }
  }
};
</script>

<style lang="stylus" scoped>
.text-color {
  color: #549DB0;
}

.header-text {
  color: white;
  margin-top: -50px;
  margin-bottom: 20px;
}

.outer-columns {
  background-color: #F2FBFD;
  padding-top: 20px;
}

.nav-back {
  color: #549DB0;
  background-color: #F0FBFD;
  padding: 5px;
}
</style>

这是我的测试:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import Articles from '../../../app/javascript/components/member-dashboard/Articles.vue'

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

describe('Articles', () => {
    let store
    let getters
    let state = {
        articles: [
            {
                title: "Testing Vue Components"                
            },
            {
                title: "This One shows",
                text: "<p>You can see me!</p>"
            },
            {
                title: "Another One",
                text: "<p>See me too!</p>"
            }
        ],
        userArticles: [
            {article: {
                title: "This One shows",
                text: "<p>You can see me!</p>"
            }},
            {article: {
                title: "Another One",
                text: "<p>See me too!</p>"
            }}
        ]
    }

    beforeEach(() => {
        getters = {
            articles: () => {
                return state.articles
            },
            userArticles: () => {
                return state.userArticles
            }
        }

        store = new Vuex.Store({ getters })
    })

    it('only displays article with body text', () => {
        const wrapper = shallowMount(Articles, {
            store,
            localVue
        })

        expect(wrapper.vm.articles.length).to.deep.equal(2)
    })
})

正如我提到的,在浅层安装中,我尝试这样做:

 const wrapper = shallowMount(Articles, {
            store,
            localVue,
            mocks: {
                $route: {
                    query: null
                }
            }
        })

但是我继续收到此错误:

TypeError: Cannot read property 'query' of undefined
      at VueComponent.articleParam (webpack-internal:///1:107:35)

当我从articleParam方法中删除行return parseInt(this.$route.query.article_id);时,我的测试通过了。

如何解决对组件中this。$ route.query的调用?我的测试不是必需的,但会导致我在安装组件时测试失败。

1 个答案:

答案 0 :(得分:2)

import VueRouter from 'vue-router';导入到您的联合测试文件中,并创建路由器的新对象(例如const router = new VueRouter();)并在测试用例中使用它。

我在这里更新了代码:

import { shallowMount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'
import Articles from '../../../app/javascript/components/member-dashboard/Articles.vue'

const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(VueRouter)
localVue.use(BootstrapVue);
const router = new VueRouter();

describe('Articles', () => {
    let store
    let getters
    let state = {
        articles: [
            {
                title: "Testing Vue Components"                
            },
            {
                title: "This One shows",
                text: "<p>You can see me!</p>"
            },
            {
                title: "Another One",
                text: "<p>See me too!</p>"
            }
        ],
        userArticles: [
            {article: {
                title: "This One shows",
                text: "<p>You can see me!</p>"
            }},
            {article: {
                title: "Another One",
                text: "<p>See me too!</p>"
            }}
        ]
    }

    beforeEach(() => {
        getters = {
            articles: () => {
                return state.articles
            },
            userArticles: () => {
                return state.userArticles
            }
        }

        store = new Vuex.Store({ getters })
    })

    it('only displays article with body text', () => {
        const wrapper = shallowMount(Articles, {
            store,
            router,
            localVue
        })

        expect(wrapper.vm.articles.length).to.deep.equal(2)
    })
})