Javascript 用茉莉花测试嵌套承诺

Javascript 用茉莉花测试嵌套承诺,javascript,unit-testing,jasmine,promise,Javascript,Unit Testing,Jasmine,Promise,当我在浏览器中运行UI时,这是有效的,但是我的validateAsync方法中的'd'总是为null,它需要调用done方法才能将其返回到save方法。我不知道如何使用andCallFake(监视唯一名称测试所必需的),但也让它返回延迟到调用done的(jQuery)。 希望这段代码能为您提供足够的上下文,让您了解我试图实现的目标 validateAsync = function () { var d, isValid = true,

当我在浏览器中运行UI时,这是有效的,但是我的validateAsync方法中的'd'总是为null,它需要调用done方法才能将其返回到save方法。我不知道如何使用andCallFake(监视唯一名称测试所必需的),但也让它返回延迟到调用done的(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...');
});

尽管我对Jasmine了解不多,但从代码本身的优点来看,如果将代码剥离到最底层,就更容易看到发生了什么

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

validateAsync = function () {
    ...
    var d = fn_that_returns_a_promise().done(function() {
        ...
        d.done(boolean);
    }).fail(function() {
        ...
    });
    return d;
};
validateAsync = function () {
    ...
    return uiDataService.GetIsLayoutNameUniqueAsync(...).then(function(...) {
        ...
        return isValid && isUnique;
    }).fail(function(...) {
        ...
    });
};
save = function() {
    validateAsync().done(function(isValid) {
        //validation success
        if(!isValid) return;
        ...
    }.fail(function() {
        //validation failure
        ...
    });
};
这不可能是正确的,因为
.done()
不接受布尔参数,虽然我不能说它肯定是错误的,
d.done()
d.done()
处理程序中并不真正合适(尽管可能在不同的情况下)

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

validateAsync = function () {
    ...
    var d = fn_that_returns_a_promise().done(function() {
        ...
        d.done(boolean);
    }).fail(function() {
        ...
    });
    return d;
};
validateAsync = function () {
    ...
    return uiDataService.GetIsLayoutNameUniqueAsync(...).then(function(...) {
        ...
        return isValid && isUnique;
    }).fail(function(...) {
        ...
    });
};
save = function() {
    validateAsync().done(function(isValid) {
        //validation success
        if(!isValid) return;
        ...
    }.fail(function() {
        //validation failure
        ...
    });
};
因此,
save()
可以如下所示:

validateAsync = function () {
    ...
    var d = fn_that_returns_a_promise().done(function() {
        ...
        d.done(boolean);
    }).fail(function() {
        ...
    });
    return d;
};
validateAsync = function () {
    ...
    return uiDataService.GetIsLayoutNameUniqueAsync(...).then(function(...) {
        ...
        return isValid && isUnique;
    }).fail(function(...) {
        ...
    });
};
save = function() {
    validateAsync().done(function(isValid) {
        //validation success
        if(!isValid) return;
        ...
    }.fail(function() {
        //validation failure
        ...
    });
};

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

您正在将布尔值传递给
.done()
,它需要一个函数。@Beetroot Beetroot那里的done()应该在save方法中触发.done。$。Deferred()有一个.resolve方法,但我认为$.Deferred().promise()没有.resolve方法。没错,只有Deferred()有状态更改方法,而从Deferred派生的promise是一个“使用者”对象,它(像Deferred本身一样)可以响应状态更改。但是请不要在这一点上挂断电话,因为
validateAsync()
save()
中的所有内容都在使用者端-状态更改在
uiDataService.GetIsLayoutNameUniqueAsync()方法中管理。我不打算对Jasmine进行评论,只是想说,编写和调试代码来调试其他复杂程度相同或更简单的代码是完全没有意义的。你读过我的答案吗?谢谢你的回答,我正在做其他事情,刚刚回到这个话题。我使用Jasmine的原因是要进行行为驱动的开发规范测试,以确保我希望发生的事情在以后的开发中不断发生。这是一条路。我当时不明白你可以从一个地方回来,我想我必须解决fn_返回的承诺,即_返回一个_承诺方法。谢谢你的帮助!有趣的是,我认为我说得对,在
validateAsync()
中,两种方法
.then()
.fail()
是可交换的——换句话说,它们可以按任意顺序链接
.then().fail()
.fail().then()
。这不是一般性的,只是在这种情况下。