我如何模拟一个被 vue 指令触发的方法?

How do I mock a method that gets fired by a vue directive?

我试图在我的 Test 组件中调用 close() 方法,但只有在指令打开的 div 外部单击时才会触发它。我应该在我的测试中做什么来确保该方法被触发?我在我的组件中使用 v-click-outside npm 包。

组件

<script>
  import vClickOutside from 'v-click-outside';

  export default {
    name: 'Test',

    directives: {
      vClickOutside,
    },

    data: () => ({
      isOpen: false,
    }),
    methods: {
      close() {
        this.isOpen = false;
      },
  };
</script>

<template>
  <div
    v-click-outside="close"
    class="test-class"
  >
    <OtherComponent />
  </div>
</template>

这是我的测试文件。

const clickOutsidelDirective = jest.fn();

describe('Test.vue', () => {
  const wrapper = shallowMount(Component, {
   directives: {
      clickOutside: clickOutsidelDirective,
    },
  });
   wrapper.find('.test-class').trigger('click');
   //not sure what i have to do to mock the close() function 

   //This doesn't get called
   expect(clickOutsidelDirective).toHaveBeenCalled();
}

在您的主要组件中,该指令是显式导入的。因此,在您的测试中,您不需要再次定义它。 v-click-outside 有一个后果,你应该测试它。这意味着,应该触发 close 方法,模拟它而不是整个指令。类似的东西:

编辑: 您的定义包括 the directive is wrong:

<template>
  <div>
    <div
        v-click-outside="close"
        class="test-class"
    >
      <h1>H1</h1>
    </div>
    <div>
      <h1 class="outside-class">Outside</h1>
    </div>
  </div>
</template>
<script>
import clickOutside from 'v-click-outside';

export default {
  name: 'Test',

  directives: {
    clickOutside: clickOutside.directive,
  },

  data() {
    return {
      isOpen: false,
    };
  },
  methods: {
    close() {
      this.isOpen = true;
    },
  }
}
</script>

在新版本的 Vue-test-utils 中,方法覆盖将被弃用,因此,像这样的东西应该可以工作:

const wrapper = shallowMount(HelloWorld)
wrapper.find('.test-class').trigger('click')
expect(wrapper.vm.isOpen).toBeTruthy()
wrapper.find('.outside-class').trigger('click')
expect(wrapper.vm.isOpen).toBeFalsy()

但事实并非如此。它与内部 v-click-outside 实现有关。我认为指令和 shallowMount 有问题。

您的组件中的指令设置不正确:

import vClickOutside from 'v-click-outside'

export default {
  directives: {
    // BEFORE: ❌ 
    vClickOutside,

    // AFTER: ✅
    clickOutside: vClickOutside.directive
  },
}

验证单击组件外部时是否调用了 close()

  1. jest.spyOn.
  2. 为测试组件创建一个 div,并 attach 为它安装包装器。
  3. v-click-directive adds its event listeners on the next macro-tick (using setTimeout with no timeout),所以测试也需要等待一个macro-tick来让指令初始化。
  4. 在包装器上触发一个 click 事件,并 await 结果。然后,断言 close() 被调用了。

测试应如下所示:

it('click directive', async () => {
  1️⃣
  const closeFn = jest.spyOn(HelloWorld.methods, 'close')

  2️⃣ 
  const div = document.createElement('div')
  document.body.appendChild(div)

  const wrapper = mount({
    template: `<div><HelloWorld /></div>`,
    components: {
      HelloWorld
    },
  }, { attachTo: div })

  try {
    3️⃣
    await new Promise(r => setTimeout(r))

    4️⃣
    await wrapper.trigger('click')
    expect(closeFn).toHaveBeenCalled() ✅

  } finally {
    wrapper.destroy()
  }
})