【问题标题】:How do I mock a method that gets fired by a vue directive?如何模拟被 vue 指令触发的方法?
【发布时间】:2021-02-24 15:21:12
【问题描述】:

我试图在我的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();
}

【问题讨论】:

    标签: javascript vue.js vuejs2 jestjs vue-test-utils


    【解决方案1】:

    在您的主要组件中,该指令是显式导入的。因此,在您的测试中,您不需要再次定义它。 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有问题。

    【讨论】:

    • 似乎即使这样做也不会触发方法/指令被调用
    • 我已经在浏览器中测试了这个模板,一切都很顺利。但是在单元测试中没有用。它应该与指令工作的环境有关。
    【解决方案2】:

    该指令未在您的组件中正确设置:

    import vClickOutside from 'v-click-outside'
    
    export default {
      directives: {
        // BEFORE: ❌ 
        vClickOutside,
    
        // AFTER: ✅
        clickOutside: vClickOutside.directive
      },
    }
    

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

    1. Mock the close methodjest.spyOn
    2. 为测试组件创建一个div,并为它创建一个已安装的包装器attach
    3. v-click-directive adds its event listeners on the next macro-tick (using setTimeout with no timeout),所以测试还需要等待一个宏滴答来初始化指令。
    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()
      }
    })
    

    【讨论】:

    • @FirzokNadeem 这个特定指令的实现检查元素外部的点击,因此我们将该元素包装在可以在测试中点击的div 中。目标元素需要附加到 div 才能使测试正常工作。
    猜你喜欢
    • 2018-11-11
    • 2020-06-16
    • 2019-08-05
    • 1970-01-01
    • 1970-01-01
    • 2018-04-30
    • 1970-01-01
    • 2019-12-10
    • 2021-04-21
    相关资源
    最近更新 更多