【问题标题】:How do I test a function that calls an AJAX function without firing it using jasmine?如何在不使用 jasmine 触发的情况下测试调用 AJAX 函数的函数?
【发布时间】:2015-08-12 21:44:29
【问题描述】:

我检查了How do I verify jQuery AJAX events with Jasmine?How to test an Angular controller with a function that makes an AJAX call without mocking the AJAX call?,但由于某种原因,它们不一定会提供适合我的格式。

我的问题是我正在对一个函数进行单元测试,该函数调用一个触发 AJAX 请求的函数。我想在不触发 AJAX 请求(或停止该请求)的情况下测试外部函数,这样我就不会将一堆错误数据射入数据服务器。

这是我的外部函数(调用包含 AJAX 的函数):

vm.clickSubmit = function () {
    vm.loading = true; // starts the loading spinner
    vm.updateData(); // makes current data current in the data model
    vm.inputData = {    // build the data to be submitted
        ...
    };
    vm.submit(); // calls the AJAX function
};

这是我的 AJAX 函数:

vm.submit = function () {
    var d = $.ajax({
        type: "POST"
        , data: angular.toJson(vm.inputData)
        , url: "http://submit_the_data"
        , contentType: "application/json"
        , xhrFields: { withCredentials: true }
        , crossDomain: true
    })
    .success(function (resp) {
        DataService.ticketNumber = resp; // returns ticket# for the data model
    })
    .error(function (error) {
        DataService.ticketNumber = DataService.submitError;
    });
    d.then(function (d) {
        vm.loading = false; // stops the loading spinner
        DataService.tickets = []; // empty's the array to be filled anew
        $location.path('/submitted'); // success splash html
        $scope.$apply();
    });
};

我已经编写了将读取和验证inputData 对象中的值的所有测试,但我不确定如何包围对clickSubmit() 的调用,因此实际上没有任何内容提交给服务器。我的单元测试已经到了这一点:

'use strict';

describe('Controller: HomeController', function () {
    beforeEach(module('tickets'));
    var controller, scope, $location, DataService;
    var tests = 0;
    beforeEach(inject(function ($rootScope, $controller, _$location_, _DataService_) {
        $location = _$location_;
        DataService = _DataService_;
        scope = $rootScope.$new();
        controller = $controller('HomeController', {
            $scope: scope
        });
    }));
    afterEach(function () {
        tests += 1;
    });
    describe('clickSubmit should verify data and submit new ticket', function () {
        beforeEach(function () {
            jasmine.Ajax.install();
            controller.loading = false;
                ... // inputData fields filled in with test data
        });
        afterEach(function () {
            jasmine.Ajax.uninstall();
        });
        it('should start the spinner when called', function () {
            controller.clickSubmit();
            expect(controller.loading).toBeTruthy();
        });
        // other it('') tests
    });
    it('should have tests', function () {
        expect(tests).toBeGreaterThan(0);
    });
});

那么在加载微调器的期望之后应该怎么做才能在实际代码中取消对vm.submit() 的调用?

谢谢,

-C§

【问题讨论】:

  • 首先,我建议您将 ajax 调用移至单独的服务中。似乎您现在只是将服务调用中的数据设置到您的角度服务/工厂中。然后你应该测试服务/工厂本身。 Angular 为此提供了 $httpBackend (docs.angularjs.org/api/ngMock/service/$httpBackend),因此您不必实际调用该服务。对于控制器测试,您应该模拟服务/工厂,因此您不必关心您的服务/工厂做什么。
  • @m.brand 您介意填写下面的答案来说明此方法吗?我只是问,因为我对 Angular/Jasmine/Karma 还是很陌生。此外,$httpBackend 是否可以与引用的 AJAX 调用一起使用,还是我必须将其转换为 $http?谢谢,C§
  • 这个答案展示了一些将 ajax 调用拉入服务的解决方案:stackoverflow.com/questions/17646034/…

标签: javascript jquery angularjs unit-testing karma-jasmine-ajax


【解决方案1】:

我建议模拟调用而不是实际调用它。粒度由您决定,您可以存根 ajax 调用,也可以存根整个提交函数。

这里是你如何存根提交函数:

spyOn(controller, 'submit').and.callFake(function() {
    DataService.ticketNumber = somevalue;
});

在实际调用 caller.clickSubmit() 的控制器之前放置该代码。

然后您可以跟进对间谍的期望,例如:

expect(controller.submit).toHaveBeenCalled()

或与 spyOn 相关的任何其他期望。

这里是 jasmine 文档:http://jasmine.github.io/2.0/introduction.html
往下看“间谍”区域。

如果你想模拟 ajax 调用,你必须模拟一个这样的承诺:

spyOn($, 'ajax').and.callFake(function() {
    var deferred = $q.defer();
    deferred.resolve(someResponse);
    return deferred.promise;
});

另外,为了让代码等待 Promise 解析,在提交调用之后,您需要运行 $scope.$digest() 以便 Angular 可以处理 Promise 解析。然后,您可以检查您对取决于承诺的解决或拒绝的代码的期望。

【讨论】:

  • 我的updateData() 函数出现错误“错误:预期是间谍,但得到了函数。”。在加载 inputData 块之后,我将 spyOn 块放在 beforeEach 块中。这是该系列中唯一失败的测试。 expect(controller.submit).toHaveBeenCalled(); 通过了,所以我很困惑为什么 updateData() 测试失败了。
  • 没关系,使用spyOn(controller, 'updateData').and.callThrough(); 让它工作。谢谢您的帮助。在模拟数据服务时,我可能会经常提到这一点(自从我将它移出控制器以来,它现在包含所有 3 个 AJAX 调用)。 - C§
  • 解决了我自己的问题。我收到错误“$q 未定义”并意识到我只需要像这样注入它:beforeEach(inject(function($q){ ... })); 我真的很感谢你在这里提供给我的东西。谢谢,C§
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-15
  • 1970-01-01
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 2017-05-13
  • 1970-01-01
相关资源
最近更新 更多