代码之家  ›  专栏  ›  技术社区  ›  mcv

jest.fn()声明没有被调用,但是

  •  0
  • mcv  · 技术社区  · 5 年前

    我正在测试一个Vue组件,当路由中存在某个参数时,该组件在Vuex存储中调用某个操作。我在嘲笑这个动作 jest.fn()

    下面是组件的相关代码:

    await this.$store.dispatch('someOtherAction');
    if (this.$route.params && this.$route.params.id) {
        this.$store.dispatch('selection/selectElement', parseInt(this.$route.params.id, 10));
    }
    

    以下是模拟函数:

    someOtherAction = jest.fn();
    selectElement = jest.fn(() => console.log("selectElement has been called"));
    

    it('selects element if passed in route', async () => {
      const $route = {params: {id: '256'}};
      const wrapper = shallowMount(AbcModel, {
        mocks: {$route},
        store, localVue
      });
      expect(someOtherAction).toHaveBeenCalled();
      expect(selectElement).toHaveBeenCalled();
    });
    

    在输出中,我可以看到“selectElement已被调用”。很明显它被称为。然而, expect(selectElement).toHaveBeenCalled() 失败。

    这怎么可能?它与我嘲笑的另一个函数配合得很好。替换模拟函数的顺序无关紧要。消除另一个函数被调用的期望也不重要,所以看起来不像是冲突。

    0 回复  |  直到 5 年前
        1
  •  4
  •   Brian Adams    5 年前

    这怎么可能?

    这个 expect 在之前运行并失败 selectElement 有机会逃跑。


    细节

    消息队列

    message queue 。当前消息 runs to completion 在下一个开始之前。

    PromiseJobs队列

    ES6引入了 PromiseJobs queue 当前消息完成后下一消息开始前 .

    异步/等待

    async await 只是 syntactic sugar over promises and generators 等待 Promise 决定。

    会发生什么

    测试将作为当前运行的消息开始运行。打电话 shallowMount 加载运行到 await this.$store.dispatch('someOtherAction'); someOtherFunction 然后将函数的其余部分作为 承诺 承诺 决定。

    然后执行返回到运行两个 期待 声明。从那以后的第一次 已被调用,但第二次失败 选择元素 尚未运行。

    然后,当前正在运行的消息完成,并运行PromiseJobs队列中的挂起作业。调用的回调 选择元素 在队列中,所以它运行并调用 登录到控制台。


    解决方案

    确保 调用的回调 在运行 期待 .

    只要有可能,最好把 承诺 所以测试可以 等待

    如果这是不可能的,那么解决方法是 等待 关于一个决议 承诺 承诺 要先运行的回调:

    it('selects element if passed in route', async () => {
      const $route = {params: {id: '256'}};
      const wrapper = shallowMount(AbcModel, {
        mocks: {$route},
        store, localVue
      });
      expect(someOtherFunction).toHaveBeenCalled();
      // Ideally await the Promise directly...
      // but if that isn't possible then calling await Promise.resolve()
      // queues the rest of the test at the back of PromiseJobs
      // allowing any pending callbacks to run first
      await Promise.resolve();
      expect(selectElement).toHaveBeenCalled();  // SUCCESS
    });