【问题标题】:Test Vue Component(created using TypeScript) with Vue-test-utils使用 Vue-test-utils 测试 Vue 组件(使用 TypeScript 创建)
【发布时间】:2020-12-01 18:28:12
【问题描述】:

我正在尝试使用 Vue-test-utils 包在 Vue 组件(使用 TypeScript 创建)中测试具有多种数据类型的道具。我尝试使用expect().tobe(),但仍有一条未覆盖的线:

DropDownList.vue

<template>
  <v-select
    :id="dropDownListID"
    :label="labelDisplay"
    :placeholder="getPlaceHolder"
    :item-text="itemtext"
    :item-value="itemvalue"
    :items="items"
    :value="value"
    @input="handleInput"
    :required="required"
    :rules="[required ? rules.required :false,additionalRulesFormated]"
    :class="{ 'roboto10':true,'xddl':true,['xddl-'+size] :true}"
    :return-object="returnobjectvalue"
    append-icon="expand_more"
    :disabled="disabled"
    :clearable="clearableValue"
  >
    <template slot="item" slot-scope="data">
      <v-flex>{{ data.item[itemtext] }}</v-flex>
    </template>
    <template slot="selection" slot-scope="data">
      <v-flex xs2 v-if="itemicon">
        <v-icon color="#3F3F3F" size="25px">{{ data.item[itemicon] }}</v-icon>
      </v-flex>
      <v-flex>{{ data.item[itemtext] }}</v-flex>
    </template>
  </v-select>
</template>

<script lang="ts">
import Vue from "vue";
import { logger } from "@/app/helpers/Logger";

export default Vue.extend({
  name: "x-drop-down-list",
  data: function() {
    return {
      rules: {
        required: (value: any) => {
          let label = (this as any).getRulesLabel;
          return !!value || label + " is required";
        }
      }
    };
  },
  props: {
    id: String,
    value: [String, Number, Boolean, Object, Array],
    size: String,
    label: String,
    placeholder: String,
    required: Boolean,
    itemtext: String,
    itemvalue: String,
    itemicon: String,
    items: Array,
    returnobject: String,
    ruleslabel: String || null,
    disabled: Boolean,
    additionalRules: [String, Boolean] || null,
    clearable: Boolean
  },
  computed: {
    dropDownListID: function() {
      let lbl = String(this.label === undefined || this.label === null ? null : String(this.label).replace(/ /g, ""));
      let id = String(this.id === undefined || this.id === null ? lbl : this.id);
      return id;
    },
    returnobjectvalue: function() {
      let ret = this.returnobject === undefined || this.returnobject === null || this.returnobject === "" ? false : String(this.returnobject).toLowerCase() == "true" ? true : false;
      return ret;
    },
    getRulesLabel: function() {
      let id = this.label || this.id;
      let c = this.ruleslabel == undefined || this.ruleslabel == null ? id : this.ruleslabel;
      return c;
    },
    getPlaceHolder: function() {
      if (this.placeholder === undefined || this.placeholder === null || this.placeholder === this.label) return " ";

       let lbl = this.label == undefined || this.label == null ? "" : String(this.label).replace(" *", "");
      let c = this.placeholder == undefined || this.placeholder == null ? lbl : this.placeholder;
      return c;
    },
    labelDisplay: function() {
      return (this.label || "") != "" ? this.label + (this.required ? " *" : "") : "";
    },
    additionalRulesFormated: function() {
      return String(this.additionalRules || "") != "" ? this.additionalRules : false;
    },
    clearableValue: function() {
      return this.clearable === undefined || this.clearable === null ? true : this.clearable;
    }
  },
  methods: {
    handleInput: function(val: string) {
      this.$emit("input", val);
    }
  }
});
</script>

<style lang="scss">
</style>

dropdownlist.spec.ts

/**
 * Unit test for component DropdownList.vue.
 */
import Vue from "vue";
import Vuetify from "vuetify";
import DropdownList from "@/components/dropdown/DropdownList.vue";
import { createLocalVue, shallowMount, Wrapper } from "@vue/test-utils";

describe("DropdownList.vue", () => {
  let vuetify: any;
  let mountFunction: (options?: object) => Wrapper<Vue>;
  const localVue = createLocalVue();
  Vue.use(Vuetify);

  const id = "dropdownlistcomponentid";
 
  beforeEach(() => {
    vuetify = new Vuetify();
    mountFunction = (options = {}) => {
      return shallowMount(DropdownList, { localVue, vuetify, ...options });
    };
  });

  it("props.ruleslabel as string || null", () => {
    var mywrapper = mountFunction({ 
      propsData: { 
        id: id, 
        ruleslabel: "ruleslabel" || null 
      } });
    
    expect(mywrapper.vm.$props.ruleslabel).toBe("ruleslabel" || null);
  });
});

为了测试道具:ruleslabeladditionalRules,我运行了 npm run test:unit。测试通过了!,但第 59 行仍然是未覆盖的行:

【问题讨论】:

    标签: typescript vue.js jestjs


    【解决方案1】:

    String || null 实际上是short-circuitsString条件表达式,因为String 构造函数是真实的,所以|| null 在这里没有有意义的值,应该删除以避免被视为需要代码覆盖的表达式。同样,|| null 应从 additionalRules 中删除,并在您对 "ruleslabel" 的测试中删除。

    我认为您可能一直在尝试指定一个可为空的 string,如下所示:

    let ruleslabel: string | null
    

    ...但是props 在此上下文中的声明是构造函数的字符串(不是类型)的字典。 String 是一个构造函数,但string | null 是一个类型

    指定此prop 类型的正确方法是使用PropType

    import Vue, { PropType } from 'vue'
    
    export default Vue.extend({
      props: {
        ruleslabel: String as PropType<string | null>,
      }
    })
    

    但是,props 已经是可选的,因此无需指定它可以是 null。此外,考虑到x === undefined || x === null 等价于!x,可以简化getRulesLabel() 以消除在ruleslabel 中对null(或undefined)的显式检查:

    export default {
      computed: {
        getRulesLabel() {
          // BEFORE:
          //let id = this.label || this.id;
          //let c = this.ruleslabel == undefined || this.ruleslabel == null ? id : this.ruleslabel;
          //return c;
    
          return this.ruleslabel || this.label || this.id;
        }
      }
    }
    

    您的代码中的许多其他条件都可以按照此示例进行简化。

    【讨论】:

      【解决方案2】:

      要测试“falsy”分叉,您需要在 ruleslabel 为 falsy 时至少测试一次。 在您的测试中,您使用ruleslabel: "ruleslabel" || null,这里"ruleslabel" || null 的表达式将始终为真。

      你可以这样测试:

      it("works when props.ruleslabel is a string", () => {
          ...
          ruleslabel: "ruleslabel"
          ...
      
      it("also works when props.ruleslabel is falsy", () => {
          ...
          ruleslabel: false
          ...
      

      使用||运算符时,如果第一个值为真, 第二个值将被忽略,因此出于所有意图和目的,这两个表达式是相等的:

      ruleslabel: "ruleslabel" || null
      ruleslabel: "ruleslabel"
      

      任何时候你看到guaranteedTruthy || anythingElse,你就知道anythingElse 永远不会被使用。在 javascript 中,所有字符串都是真实的,除了空的 ""。

      【讨论】:

        猜你喜欢
        • 2019-07-16
        • 2019-10-24
        • 2019-04-19
        • 1970-01-01
        • 2018-09-28
        • 2021-04-03
        • 2021-02-17
        • 2018-11-09
        • 2021-09-11
        相关资源
        最近更新 更多