如何模拟jQuery .done(),以便它能与Jest正确执行?

时间:2018-07-11 07:32:05

标签: javascript reactjs jestjs enzyme

我正在尝试为更改React模块的密码编写单元测试,但是我无法获得要在方括号中执行的代码。我已经为模块MyAPI编写了一个模拟程序,该模拟代码可以很好地执行,并且可以使用console.log(“ something”)在控制台中看到输出。

但是,我无法让代码在.done(function(data(function,data))。之后运行。最有可能是因为该模拟用自己的代码替换了这些代码。

我知道一种选择是使用像Nock这样的假服务器,但是除非有必要,否则我不想将其变成集成测试。

我要测试的代码:

const MyAPI = require('../../my_api.js');
submitChangePasswordFormEvent(event) {
    const self = this;
    const params = {};
    event.preventDefault();
    event.stopPropagation();

    params.current_password = this.refs.current_password.getValue();
    params.passwordFirst = this.refs.passwordFirst.getValue();
    params.passwordSecond = this.refs.passwordSecond.getValue();

    MyAPI.my_api('/api/change_password/', params)
        .done(function (data) {
            // This code i would like to run but can't
            const elem = <Success>{t(['settings', 
            'passwords_changed'])}</Success>;
            self.setState({ pwerror: null, pwsuccess: elem });
            self.refs.current_password.value = '';
            self.refs.password1.value = '';
            self.refs.password2.value = '';
        })
        .error(function (errors) {
           // This code i would like to run but can't
            let msg = '';
            $.each(errors.responseJSON, function (k, v) {
                msg += v;
            });
            msg = <Error>{msg}</Error>;
            self.setState({ pwerror: msg, pwsuccess: null });
        });
}

MyAPI的模拟文件

var MyAPI = function () {};


 MyAPI.prototype.my_api = function(url) {
 return $.ajax();
}
module.exports = new MyAPI();

还有Jest设置脚本:

const jqueryMock = {
ajax: function (argument) {
  return {done: function (data) {
    return {error: function (errors) {
      return "success";
    }}}}
}}

global.$ = jqueryMock;

1 个答案:

答案 0 :(得分:3)

您希望执行.done.error方法,但又不想实际发出请求(顺便说一句,我不知道.error方法只是{{ 1}})?然后,我将执行以下操作:

全局模拟jQuery

在工作目录顶层的.fail目录内为jquery创建全局模拟:

__mocks__

通过在要测试的模块中请求时,将//__mocks__/jquery.js: const jQ = jest.requireActual("jquery"); const ajax = jest.fn(() => { return jQ.Deferred(); }); export const $ = { ...jQ, // We don't want to mock jQuery completely (we might want to alter $.Deferred status) ajax, }; export default $; 放在jquery.js目录中,jQuery就会被笑话自动嘲笑(嗯,在这种情况下,它会被部分嘲笑...)。

使用此设置,您可以运行代码而无需发出实际请求,而通常运行__mocks__.done方法以及已注册的回调。

模拟.done和.fail方法

如果您不想要在.error.done中执行已注册的回调,则需要手动模拟它们,而不是返回.fail return带有笑话模拟的普通javascript对象。

在特定的测试用例中,您肯定不希望jQ.Deferred() / .done调用注册的回调:

.error

模拟成功或错误

要在特定测试用例中模拟成功或错误时,请再次覆盖全局模拟实现:

为了成功:

// By returning "this" we are able to chain in the way $.ajax("/api", params).done().fail()

const jqXHR = {
    done: jest.fn().mockImplementation(function () {
        return this;
    }),
    fail: jest.fn().mockImplementation(function () {
        return this;
    }),
    // some more $.Deferred() methods you want to mock
};

// Overwrite the global $.ajax mock implementation from __mocks__/jquery.js with our custom one
$.ajax.mockImplementation(() => jqXHR)

对于错误:

// success
const dfd = $.Deferred();
$.ajax.mockImplementation(() => {
    return dfd.resolve("success"); // this is what your done callback will receive as argument
});

请注意,断言// success const dfd = $.Deferred(); $.ajax.mockImplementation(() => { return dfd.reject("error"); // this is what your fail callback will receive as argument }); .done已被调用/未调用是没有意义的,因为总是会调用它们,因为它们都会注册您放置在其中的回调。仅当.fail解析或拒绝特定的注册回调时,您才可以对其进行测试。

为了更好地进行单元测试,应从$.Deferred / .done中排除匿名函数。由于JavaScript很奇怪,并且不像python(我更喜欢python),因此您不能轻易地在被测模块中模拟特定的函数。因此,您需要将它们放在专用模块中,然后完全模拟该模块。然后,您可以断言它们是成功调用还是错误调用。

花了我一段时间才能弄清楚如何正确地使用jquery进行模拟,所以我想在这里分享我的经验。希望这会有所帮助...