使用moxios和fakeTimers测试去抖动的异步请求

时间:2017-12-20 13:20:24

标签: vue.js axios sinon moxios vue-test-utils

我正试图在debounced方法中测试一个axios调用,但是如果我添加假定时器,moxios.wait()总是超时。 如果将去抖时间设置得足够小(例如10ms),则测试在没有时钟的情况下工作,但这无助于测试正确的去抖动。 我已经尝试过试验Vue.nextTick以及回调它()async,但我似乎只是进入了杂草。什么是正确的方法?

这是一个组件和测试合二为一,显示了问题:

import Vue from 'vue'
import { mount } from 'vue-test-utils'
import axios from 'axios'
import moxios from 'moxios'
import _ from 'lodash'
import expect from 'expect'
import sinon from 'sinon'

let Debounced = Vue.component('Debounced',
    {
        template: `<div><button @click.prevent="fetch"></button></div>`,
        data() {
            return {
                data: {}
            }
        },
        methods: {
            fetch: _.debounce(async () => {
                let data = await axios.post('/test', {param: 'example'})
                this.data = data
            }, 100)
        }
    }
)

describe.only ('Test axios in debounce()', () => {
    let wrapper, clock

    beforeEach(() => {
        clock = sinon.useFakeTimers()
        moxios.install()
        wrapper = mount(Debounced)
    })

    afterEach(() => {
        moxios.uninstall()
        clock.restore()
    })

    it ('should send off a request when clicked', (done) => {
        // Given we set up axios to return something
        moxios.stubRequest('/test', {
            status: 200,
            response: []
        })

        // When the button is clicked
        wrapper.find('button').trigger('click')
        clock.tick(100)

        moxios.wait(() => {
            // It should have called axios with the right params
            let request = moxios.requests.mostRecent()
            expect(JSON.parse(request.config.data)).toEqual({param: 'example'})

            done()
        })
    })
})

1 个答案:

答案 0 :(得分:1)

关于测试超时异常: moxios.wait依赖于setTimeout,但我们将setTimeout替换为自定义js实现,并制作了moxios.wait工作,我们应该在等待调用后调用clock.tick(1)

    moxios.wait(() => {
        // It should have called axios with the right params
        let request = moxios.requests.mostRecent()
        expect(JSON.parse(request.config.data)).toEqual({param: 'example'})

        done()
    });
    clock.tick(1);

这将允许测试进入回调主体...但是它将再次失败,除非未定义request

主要问题:问题是假计时器正在同步调用所有回调(不使用macrotask事件循环),但是Promises仍使用事件循环。

因此,您的代码仅在执行任何Promise“ then”之前结束。当然,您可以将测试代码作为微任务推送来访问请求和响应数据,例如(使用await或将其包装为“回调”)

    wrapper.find('button').trigger('click');

    // debounced function called
    clock.tick(100);
    // next lines will be executed after an axios "then" callback where a request will be created.
    await Promise.resolve();


    // axios promise created, a request is added to moxios requests
    // next "then" is added to microtask event loop
    console.log("First assert");
    let request = moxios.requests.mostRecent()
    expect(JSON.parse(request.config.data)).toEqual({ param: 'example' });

    // next lines will be executed after promise "then" from fetch method
    await Promise.resolve();

    console.log("Second assert");
    expect(wrapper.vm.data.data).toEqual([]);
    done();

我为数据添加了另一个断言,以表明您的代码应“等待”两个“ then”回调:axios内部“ then”与您的请求创建以及您的{then}从fetch方法。 如您所见,我删除了wait呼叫,因为如果您要等待Promises,它实际上什么也没做。

是的,这是一个肮脏的hack,与axios实现本身密切相关,但是我没有更好的建议,首先我只是试图解释这个问题。