【问题标题】:Testing nested promises with Jasmine使用 Jasmine 测试嵌套的 Promise
【发布时间】:2013-03-30 23:10:12
【问题描述】:

当我在浏览器中运行 UI 时,这是有效的,但是我的 validateAsync 方法中的“d”总是为 null,需要调用 done 方法以将其返回到 save 方法。我不知道如何使用andCallFake(需要监视唯一名称测试),但也让它返回延迟调用完成的(jQuery)。 希望这段代码能给你足够的上下文来了解我想要完成的工作。

    validateAsync = function () {
        var d,
            isValid = true,
            isUnique = false;
            // validate that name and description are given
            if (layout.Name() === '') {
                toastr.warning('Layout name is required', 'Layout');
                isValid = false;
            }
             // validate that there are no other layouts of the same type with the same name
            d = uiDataService.GetIsLayoutNameUniqueAsync(layout.LayoutId(), layout.Name(), layout.LayoutTypeId())
                .done(function (isUniqueResult) {
                    isUnique = isUniqueResult.toLowerCase() === "true";
                    if (!isUnique) {
                        toastr.warning('Layout name ' + layout.Name() + ' must be unique. There is already a layout with this name.', 'Layout');
                    }
                    // this is always undefined in my Jasmine tests
                    d.done(isValid && isUnique);
                })
                .fail(function (response) {
                    mstar.AjaxService.CommonFailHandling(response.responseText);
                });
            return d;
    },
    save = function () {
        validateAsync()
            .done(function (isValidResult) {
                var isValid = isValidResult.toLowerCase() === "true";
                if (!isValid) {
                    return;
                }
                 // show a toastr notification on fail or success
                dataContext.SaveChanges(layout, uiDataService)
                    .done(function (layoutIdFromSave) {
                        toastr.success('The layout was saved. Refreshing...');
                    })
                    .fail(function () {
                        toastr.error('There was an error saving the layout.');
                    })
                    .always(function () {
                        // toastr.info('finished');
                    });
            })
            .fail(function () {
                throw new Error('There was an error validating before save');
            });
    };      

    // in uiDataService
     getIsLayoutNameUniqueAsync = function (layoutId, layoutName, layoutTypeId) {
        return ajaxService.AjaxGetJsonAsync(webServiceUrl + "GetIsLayoutNameUnique?layoutId=" + layoutId + "&layoutName=" + escape(layoutName) + "&layoutTypeId=" + layoutTypeId);
    },
    // in ajaxService
 ajaxGetJsonAsync = function (url, cache) {
            return $.ajax({
                type: "GET",
                url: url,
                dataType: "json",
                accepts: {
                    json: "application/json"
                },
                cache: cache === undefined ? false : cache
        });
    },
// in a beforeEach
var getIsLayoutNameUniquePromiseSpy = spyOn(mstar.dataService.UiDataService, "GetIsLayoutNameUniqueAsync")
    .andCallFake(function () {
        spyObj.called = true;
        // http://stackoverflow.com/questions/13148356/how-to-properly-unit-test-jquerys-ajax-promises-using-jasmine-and-or-sinon
        var d = $.Deferred();
        d.resolve('true');
        return d.promise();
    });
// and a test
it("should show a toastr", function () {
    // Act
    vm.GetLayout().Name('Test');
    vm.GetLayout().Description('Test');
    vm.Save();
    // Assert
    expect(toastr.success).toHaveBeenCalledWith('The layout was saved. Refreshing...');
});

【问题讨论】:

  • 您将布尔值传递给.done(),它需要一个函数。
  • @Beetroot-Beetroot done() 应该在 save 方法中触发 .done 。 $.Deferred() 有一个 .resolve 方法,但我不认为 $.Deferred().promise() 有一个 .resolve 方法。
  • 没错,只有 Deferred 有改变状态的方法,而从 Deferred 派生的 Promise 是一个“消费者”对象,(就像 Def​​erred 本身一样)可以响应状​​态改变。但是请不要挂断这一点,因为validateAsync()save() 中的所有内容都在消费者端——状态的变化在uiDataService.GetIsLayoutNameUniqueAsync() 方法中进行管理。除了说编写和调试代码来调试具有相同复杂性或更简单顺序的其他代码之外,我不会尝试对 Jasmine 发表评论。
  • 你看过我的回答了吗?
  • 感谢您的回复,我正在做其他事情,刚刚回到这个问题。我使用 Jasmine 的原因是为了进行行为驱动开发规范测试,以确保我想要发生的事情在开发后期继续发生。

标签: javascript unit-testing jasmine promise


【解决方案1】:

对齐,我对 Jasmine 了解不多,但根据代码本身的优点,如果将其剥离到最基本的部分,则更容易看到发生了什么。

大大简化了,validateAsync() 目前的结构如下:

validateAsync = function () {
    ...
    var d = fn_that_returns_a_promise().done(function() {
        ...
        d.done(boolean);
    }).fail(function() {
        ...
    });
    return d;
};

这不可能是正确的,因为.done() 不接受布尔参数,虽然我不能说这绝对是错误的,但d.done()d.done() 处理程序中并不真正合适(尽管可能在不同的情况)。

我建议您使用.then() 过滤成功案例(从而传递一个用您的布尔值解析的新承诺),同时保留.fail() 用于失败案例;给出如下结构:

validateAsync = function () {
    ...
    return uiDataService.GetIsLayoutNameUniqueAsync(...).then(function(...) {
        ...
        return isValid && isUnique;
    }).fail(function(...) {
        ...
    });
};

因此,save() 可以如下:

save = function() {
    validateAsync().done(function(isValid) {
        //validation success
        if(!isValid) return;
        ...
    }.fail(function() {
        //validation failure
        ...
    });
};

现在您所要做的就是“连接点”(即重新插入您自己的陈述等)并希望我没有犯任何错误。

【讨论】:

  • 这是要走的路。我不明白你可以从 a then 返回,并认为我必须解决 fn_that_returns_a_promise 方法返回的承诺。感谢您的帮助!
  • 有趣的是,我认为在validateAsync() 中,.then().fail() 两种方法是可交换的——换句话说,它们可以按任意顺序链接; .then().fail().fail().then()。这不是一般性的,只是在这种情况下。
猜你喜欢
  • 1970-01-01
  • 2013-11-12
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-06
相关资源
最近更新 更多