简体   繁体   English

无法使用 Jest 在我的单元测试中呈现 Vue 组件

[英]Cannot render Vue component in my unit test with Jest

I'm trying to render a component in my Vue unit test with Jest, but I can't seem to get it to work.我正在尝试使用 Jest 在我的 Vue 单元测试中渲染一个组件,但我似乎无法让它工作。

The logs say: TypeError: Cannot read property 'length' of null日志说: TypeError: Cannot read property 'length' of null

I've tried other components, and it works fine.我试过其他组件,它工作正常。

ErrandsListTable.vue ErrandsListTable.vue

<template>
  <div
    v-if="!error"
    class="flex-table flex-table--white-background flex-table--green-header flex-table--with-dropdown"
    :class="{ loading: loading }"
  >
    <div class="flex-table__row flex-table--header text--level-6">
      <div class="flex-table__cell flex-table__cell--width-3">
        {{ statusHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-3">
        {{ idHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-7">
        {{ descriptionHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-5">
        {{ typeHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-3">
        {{ dateOpenedHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-3">
        {{ dateClosedHeader }}
      </div>
      <div class="flex-table__cell flex-table__cell--width-1" />
    </div>

    <div
      v-for="(errand, index) in sliceArray(errands)"
      :key="!errand.id ? index : errand.id"
    >
      <div
        class="flex-table__row"
        :class="{ 'flex-table__row--closed': index !== selectedErrand }"
        @click="toggleExpanded(index)"
      >
        <div class="flex-table__cell flex-table__cell--width-3">
          {{ errand.status }}
        </div>
        <div class="flex-table__cell flex-table__cell--width-3">
          {{ errand.id }}
        </div>
        <div
          class="flex-table__cell flex-table__cell--width-7 flex-table__cell--show-on-mobile"
          :data-header="descriptionHeader"
        >
          {{ errand.description }}
        </div>
        <div class="flex-table__cell flex-table__cell--width-5">
          {{ errand.type }}
        </div>
        <div
          class="flex-table__cell flex-table__cell--width-3 flex-table__cell--show-on-mobile"
          :data-header="dateOpenedHeader"
        >
          {{ errand.orderDate }}
        </div>
        <div class="flex-table__cell flex-table__cell--width-3">
          {{ errand.deliveryDate }}
        </div>
        <div
          class="flex-table__cell flex-table__cell--width-1 flex-table__cell--dropdown-show-on-mobile"
        >
          <i
            class="icon-angle__small"
            :class="{ 'icon-angle__small--down': selectedErrand === index }"
          />
        </div>
      </div>

      <ErrandsListDetails
        v-if="selectedErrand >= 0 && selectedErrand === index"
        :errand="errand"
        v-bind="$props"
      />
    </div>

    <div
      v-if="errands.length >= numberOfErrandsLoaded"
      class="flex-table--load-more text--level-5"
    >
      <a href="#" @click="loadMore">
        {{ showMoreText }}
        <i class="icon-down" />
      </a>
    </div>
    <div
      v-if="errands.length < 1 && !loading"
      class="flex-table--error text--level-5"
    >
      {{ noCasesText }}
    </div>
    <div v-if="error && !loading" class="flex-table--error text--level-5">
      {{ errorText }}
    </div>
  </div>
</template>

<script>
import _ from "lodash";
import ErrandsListDetails from "./ErrandsListDetails.vue";

export default {
  components: {
    ErrandsListDetails: ErrandsListDetails
  },
  props: {
    componentTitle: {
      type: String,
      default: () => {
        return "";
      }
    },
    showMoreText: {
      type: String,
      default: "Visa fler ärenden"
    },
    statusHeader: {
      type: String,
      default: () => {
        return "Status";
      }
    },
    dateOpenedHeader: {
      type: String,
      default: () => {
        return "Beställt/Anmält";
      }
    },
    dateClosedHeader: {
      type: String,
      default: () => {
        return "Beräknad klar";
      }
    },
    idHeader: {
      type: String,
      default: () => {
        return "Ärendenummer";
      }
    },
    descriptionHeader: {
      type: String,
      default: () => {
        return "Beskrivning";
      }
    },
    typeHeader: {
      type: String,
      default: () => {
        return "Typ";
      }
    },
    fileDetails: {
      type: String,
      default: () => {
        return "Ladda ned beställningen som PDF:";
      }
    },
    commentsDetails: {
      type: String,
      default: () => {
        return "Noteringar";
      }
    },
    wordList: {
      type: String,
      default: () => {
        return "";
      }
    },
    attachment: {
      type: String,
      default: () => {
        return "";
      }
    },
    noCasesText: {
      type: String,
      default: () => {
        return "Det finns inget att visa";
      }
    },
    errorText: {
      type: String,
      default: () => {
        return "Något gick fel";
      }
    },
    createdOnFallback: {
      type: String,
      default: () => {
        return "-";
      }
    },
    closedOnFallback: {
      type: String,
      default: () => {
        return "-";
      }
    },
    errandsCounter: {
      type: Object,
      default: () => {
        return null;
      }
    },
    errands: {
      type: Array,
      default: () => {
        return null;
      }
    },
    loading: {
      type: Boolean,
      default: () => {
        return false;
      }
    }
  },
  data() {
    return {
      selectedErrand: -1,
      error: undefined,
      numberOfErrandsLoaded: 10
    };
  },
  methods: {
    toggleExpanded: function(index) {
      this.selectedErrand = this.selectedErrand === index ? -1 : index;
    },

    loadMore: function(event) {
      event.preventDefault();
      this.numberOfErrandsLoaded += 10;
    },

    sliceArray: function(array) {
      let val = _.orderBy(array, "orderDate", "desc");
      return val.slice(0, this.numberOfErrandsLoaded);
    }
  }
};
</script>

Also ignore the fact that this component isn't done and might not necessarily involve the most relevant use cases for unit tests.还要忽略这个组件没有完成并且可能不一定涉及单元测试最相关的用例的事实。 I'm just curious to know why this certain component can't be loaded as a Vue instance in my test?我只是想知道为什么在我的测试中无法将这个特定组件作为 Vue 实例加载?

import { shallowMount } from "@vue/test-utils";
import ErrandsListTable from "../ErrandsListTable.vue";

let wrapper;

describe("Errandslist table", () => {
  beforeEach(() => {
    wrapper = shallowMount(ErrandsListTable);
  });

  afterEach(() => {
    wrapper.destroy();
  });
  it("is a Vue instance", () => {
    expect(wrapper.isVueInstance).toBeTruthy();
  });
});

Has anyone had the same issue?有没有人有同样的问题? If so, can someone explain to me how you solved it?如果是这样,有人可以向我解释你是如何解决的吗?

The default value of errands prop is null . errands prop 的默认值为null Since you're mounting your component without any props, the default value of null takes effect.由于您在没有任何道具的情况下安装组件,因此null的默认值生效。 Your template has errands.length , which results in the error you've observed.您的模板有errands.length ,这会导致您观察到的错误。

To resolve the error, you could either mount the component with an array for the errands prop :要解决该错误,您可以使用errands 道具的数组安装组件:

wrapper = shallowMount(ErrandsListTable, {
  propsData: {
    errands: []
  }
});

...or change the default value from null to an empty array: ...或将默认值从null更改为空数组:

export default {
  props: {
    errands: {
      type: Array,
      default: () => []
    }
  }
}

...or add a conditional (ie, v-if="errands" ) to your template so that it is only dereferenced when truthy: ...或添加一个条件(即v-if="errands" )到您的模板,以便它仅在 truey 时被取消引用:

<div v-if="errands && errands.length >= numberOfErrandsLoaded">
           ^^^^^^^^^^

The last solution is defensive programming that would protect against users passing in a null / undefined prop, causing the same error you're encountering.最后一个解决方案是防御性编程,它可以防止用户传入null / undefined属性,从而导致您遇到的相同错误。

In your template your errands array is null, so you cannot access length property.在您的模板中,您的errands数组为空,因此您无法访问 length 属性。 You either have to handle this case in your component or you have to pass this property to the component.您要么必须在组件中处理这种情况,要么必须将此属性传递给组件。

set default value设置默认值

errands: {
  type: Array,
  default: () => {
    return [];
  }
}

pass default value during test在测试期间传递默认值

const wrapper = shallowMount(ErrandsListTable, {
  propsData: {
    errands: []
  }
})
expect(wrapper.props().errands.length).toBe(0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM