测试使用Jasmine从jQuery AJAX回调调用的函数

时间:2022-10-23 20:39:11

I have a function that makes an AJAX call to a service. I'm attempting to expect that the displayError function is called on a failure.

我有一个函数,可以对服务进行AJAX调用。我试图期望在失败时调用displayError函数。

I have my function ajaxCall that accepts a url. Upon success I pass the result to displaySuccess and when there's an error I pass the details to displayError.

我有我的函数ajaxCall接受一个网址。成功后,我将结果传递给displaySuccess,当出现错误时,我将细节传递给displayError。

function ajaxCall(url) {
    $.ajax({
        method: "GET",
        url: url,
        data: "json",
        beforeSend: function (xhr) {
            //Do Stuff
        },
        error: function(xhr, textStatus, errorThrown) { displayError(xhr, textStatus, errorThrow, url)},
        success: function (results) { displaySuccess(result) }
    });
}

function displayError(xhr, textStatus, errorThrow, url) { 
    //Do Stuff// 
}
function displaySuccess(results) { 
    //Do Stuff// 
}

In Jasmine I have it successfully verifying the URL. My problem is in testing to insure that the displayError and displaySuccess functions are called.

在Jasmine中,我成功验证了URL。我的问题在于测试以确保调用displayError和displaySuccess函数。

I have the following for this specific issue so far.

到目前为止,我对此特定问题有以下内容。

describe('The ajaxCall component', function() {
    it('should call the error function when the ajax call fails', function () {
        var obj = {};
        spyOn(obj, 'displayError');

        spyOn($, "ajax").and.callFake(function (options) {
            options.error();

        });


        ajaxCall('/myResource/get');
        expect(obj.method).toHaveBeenCalled();
    });
}

I'm a little new to unit testing and I've searched trying to find suggestions that would help but they make the unit test fail. Where am I going wrong with this?

我对单元测试有点新意,我一直在搜索试图找到有用的建议,但他们让单元测试失败了。我哪里错了?

1 个解决方案

#1


2  

This all boils down to how you spy on your objects and writing code that is more testable. Let's work through a few strategies.

这一切都归结为你如何监视你的对象并编写更可测试的代码。让我们通过一些策略。

Strategy 1

Given your current code is not within an object, you could test that these functions are called by simply testing their implementation directly.

鉴于您当前的代码不在对象中,您可以通过直接测试其实现来测试这些函数是否被调用。

Instead of testing that the functions were called, you would test their implementation directly.

您可以直接测试它们的实现,而不是测试函数是否被调用。

Example

describe("strategy 1", function () {
    var ajaxSpy;

    beforeEach(function () {
        ajaxSpy = spyOn($, 'ajax');

        ajaxCall();
    });

    describe("error callback", function () {
        beforeEach(function() {
            spyOn(window, 'alert');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        describe("when there is an error", function() {
            it("should alert an error message", function() {
                expect(window.alert).toHaveBeenCalledWith('Error');
            });
        });
    });
});

Strategy 2

While the above works, it can be cumbersome to write tests. Ideally, you want to test the invocation and implementation separately.

虽然上述工作,但编写测试可能很麻烦。理想情况下,您希望单独测试调用和实现。

To do so, we can spy on these functions. Since these are in the global namespace, you can spy on them through the window object.

为此,我们可以监视这些功能。由于它们位于全局命名空间中,因此您可以通过window对象监视它们。

Example

describe("strategy 2", function () {
    var ajaxSpy;

    beforeEach(function () {
        ajaxSpy = spyOn($, 'ajax');

        ajaxCall();
    });

    describe("error callback", function () {
        beforeEach(function() {
            spyOn(window, 'displayError');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        describe("when there is an error", function() {
            it("should alert an error message", function() {
                expect(window.displayError).toHaveBeenCalled();
            });
        });
    });
});

Strategy 3 (Recommended)

The final strategy, and what I recommend, has a similar setup to the second strategy, except we encapsulate our implementation into a custom object.

除了我们将实现封装到自定义对象中之外,最终策略和我推荐的策略与第二个策略的设置类似。

Doing so makes the code more testable by wrapping functionality in objects and avoids the global namespace (i.e. window).

这样做可以通过将功能包装在对象中来避免全局命名空间(即窗口),从而使代码更易于测试。

Example

describe("solution 3", function() {
    var ajaxSpy;

    beforeEach(function() {
        ajaxSpy = spyOn($, 'ajax');

        ajaxService.ajaxCall();
    });

    describe("error callback", function() {
        beforeEach(function() {
            spyOn(ajaxService, 'displayError');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        it("should alert an error message", function() {
            expect(ajaxService.displayError).toHaveBeenCalled();
        });
    });
});

#1


2  

This all boils down to how you spy on your objects and writing code that is more testable. Let's work through a few strategies.

这一切都归结为你如何监视你的对象并编写更可测试的代码。让我们通过一些策略。

Strategy 1

Given your current code is not within an object, you could test that these functions are called by simply testing their implementation directly.

鉴于您当前的代码不在对象中,您可以通过直接测试其实现来测试这些函数是否被调用。

Instead of testing that the functions were called, you would test their implementation directly.

您可以直接测试它们的实现,而不是测试函数是否被调用。

Example

describe("strategy 1", function () {
    var ajaxSpy;

    beforeEach(function () {
        ajaxSpy = spyOn($, 'ajax');

        ajaxCall();
    });

    describe("error callback", function () {
        beforeEach(function() {
            spyOn(window, 'alert');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        describe("when there is an error", function() {
            it("should alert an error message", function() {
                expect(window.alert).toHaveBeenCalledWith('Error');
            });
        });
    });
});

Strategy 2

While the above works, it can be cumbersome to write tests. Ideally, you want to test the invocation and implementation separately.

虽然上述工作,但编写测试可能很麻烦。理想情况下,您希望单独测试调用和实现。

To do so, we can spy on these functions. Since these are in the global namespace, you can spy on them through the window object.

为此,我们可以监视这些功能。由于它们位于全局命名空间中,因此您可以通过window对象监视它们。

Example

describe("strategy 2", function () {
    var ajaxSpy;

    beforeEach(function () {
        ajaxSpy = spyOn($, 'ajax');

        ajaxCall();
    });

    describe("error callback", function () {
        beforeEach(function() {
            spyOn(window, 'displayError');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        describe("when there is an error", function() {
            it("should alert an error message", function() {
                expect(window.displayError).toHaveBeenCalled();
            });
        });
    });
});

Strategy 3 (Recommended)

The final strategy, and what I recommend, has a similar setup to the second strategy, except we encapsulate our implementation into a custom object.

除了我们将实现封装到自定义对象中之外,最终策略和我推荐的策略与第二个策略的设置类似。

Doing so makes the code more testable by wrapping functionality in objects and avoids the global namespace (i.e. window).

这样做可以通过将功能包装在对象中来避免全局命名空间(即窗口),从而使代码更易于测试。

Example

describe("solution 3", function() {
    var ajaxSpy;

    beforeEach(function() {
        ajaxSpy = spyOn($, 'ajax');

        ajaxService.ajaxCall();
    });

    describe("error callback", function() {
        beforeEach(function() {
            spyOn(ajaxService, 'displayError');

            var settings = ajaxSpy.calls.mostRecent().args[0];
            settings.error();
        });

        it("should alert an error message", function() {
            expect(ajaxService.displayError).toHaveBeenCalled();
        });
    });
});