【问题标题】:How to test an Angular controller with a function that makes an AJAX call without mocking the AJAX call?如何在不模拟 AJAX 调用的情况下使用进行 AJAX 调用的函数来测试 Angular 控制器?
【发布时间】:2014-10-21 12:43:23
【问题描述】:

我在控制器中有一个函数,该函数调用一个服务,该服务又调用另一个服务,该服务进行 AJAX 调用并返回承诺。简单来说:

MainController
    call = function(){
        //Handle the promise here, update some configs and stuff
        someService.call().then(success, failure);
    }

SomeService
    return {
        call: function(){
            return SomeOtherService.call();
        }
    }

SomeOtherService
    return {
        call: function(){
            return $http.get(someUrl);
        }          
    }

如何在不实际进行 AJAX 调用的情况下测试主控制器的调用功能?我显然会单独测试这些服务。

更新:所以我按照下面的答案进行了更改,这就是我得到的:

angular.module('myApp')
.controller('LoginCtrl', ['$scope', '$location', 'AuthService', '$state', function ($scope, $location, AuthService, $state) {
    $scope.user     = {};
    $scope.login    = function(user) {
        $scope.user = user;

        //This login function in turn calls the 'authenticate' from another service 'Ajax' and the promise is handled here.
        AuthService.login($scope.user)
            .then(function() {
                $scope.loginStatus = {
                    message     : 'Login successful',
                    alertClass  : 'success'
                };
                $state.go('app.dashboard');
            }, function() {
                $scope.loginStatus = {
                    message     : 'Unauthorized access.',
                    alertClass  : 'danger'
                };
            });
        };

    $scope.closeAlert = function() {
        $scope.loginStatus = false;
    };
}]);

PhantomJS 1.9.7 (Linux) Controller: LoginCtrl should call login FAILED
TypeError: 'undefined' is not an object (evaluating 'AuthService.login($scope.user)
                .then')
    at /home/rutwickg/Projects/workspace/myProject/app/scripts/controllers/login.js:15
    at /home/rutwickg/Projects/workspace/myProject/test/spec/controllers/login.js:34
    at /home/rutwickg/Projects/workspace/myProject/node_modules/karma-jasmine/lib/boot.js:117
    at /home/rutwickg/Projects/workspace/myProject/node_modules/karma-jasmine/lib/adapter.js:171
    at http://localhost:8080/karma.js:189
    at http://localhost:8080/context.html:145

这是我当前代码的样子:

'use strict';

describe('Controller: LoginCtrl', function() {

// load the controller's module
beforeEach(module('sseFeApp'));

var LoginCtrl,
    scope,
    authMock,
    ajaxMock,
    q;

// Initialize the controller and a mock scope
beforeEach( inject(function($controller, $rootScope, _AuthService_, _Ajax_, $q) {
        authMock = _AuthService_;
        ajaxMock = _Ajax_;
        q = $q;

        scope = $rootScope.$new();
        LoginCtrl = $controller('LoginCtrl', {
            $scope: scope
        });
    }));

it('should call login', function() {
        var deferred = q.defer();
        spyOn( authMock, 'login').and.returnValue(deferred.promise);
        $scope.login({username: 'Sample', password: 'Sample'});
        deferred.reject({});
        expect( authMock.login ).toHaveBeenCalled();

        //expect( ajaxMock.login).toHaveBeenCalled(); //Expect the second service call
    });
});

更新:根据新语法,它应该是and.returnValue(deferred.promise)。有效!

【问题讨论】:

  • 介意也提供实际控制人吗?
  • 请检查一下,我也添加了控制器。
  • 检查我的答案我添加了“$q / deferred”-part。

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


【解决方案1】:

你只需在你调用的函数上注入你自己的 Service 和 Spy。

describe( 'Controller: myCtrl', function () {

  var myService,
      ...;

  beforeEach( inject( function ( _myService_ ) {
    ...
    myService = _myService_;
    ...
  } ) );

  it( 'should call myService.call', inject(function ($q)
  {
    var deferred = $q.defer();
    spyOn( myService, 'call' ).andReturn(deferred.promise);
    // .and.returnValue() for jasmine 2.0
    $scope.myControllerFn();
    deferred.resolve( {} ); // for success 
    // deferred.reject() --> for error
    $scope.digest(); // do this if you want success/error to be executed.
    expect( myService.call ).toHaveBeenCalled();
  } );
} );

【讨论】:

  • 请让我试试这个。
  • 这给了我“未定义为对象”错误。此外,此代码仅测试一种服务级别。我的控制器调用一个服务,该服务又调用另一个服务。
  • 错误发生在哪里?什么实际上是未定义的?第二个“级别”几乎相同。你注入你想要模拟的服务并监视你期望被调用的函数。
  • 我更新了我的问题描述。请检查。
  • 好吧,如果您在解析或拒绝后执行 $scope.$digest() ,则会调用成功或错误。因此,如果你想测试你的成功部分,你必须 resolve() 延期,如果你想测试你的错误部分,你必须拒绝() 你的延期。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多