Angularjs和Jasmine:使用进行ajax调用的服务测试控制器

Angularjs和Jasmine:使用进行ajax调用的服务测试控制器,angularjs,unit-testing,jasmine,Angularjs,Unit Testing,Jasmine,我对测试javascript非常陌生。我的应用程序使用angularjs。我使用jasmine作为测试框架 这是我正在测试的控制器: angular.module('logonController', ["ngval", "accountFactory"]) .controller("logonController", function logOnController(accountFactory, $scope, $window) { $scope.hasServerE

我对测试javascript非常陌生。我的应用程序使用angularjs。我使用jasmine作为测试框架

这是我正在测试的控制器:

angular.module('logonController', ["ngval", "accountFactory"])
    .controller("logonController", function logOnController(accountFactory, $scope, $window) {
        $scope.hasServerError = false;
        $scope.Logon = function () {
            accountFactory.Logon($scope.data.LogOnModel)
                .then(function (data) {
                    $window.location.href = "/";
                },
                function (data) {
                    $scope.hasServerError = true;

                });
        }

    }) 
其中accountFactory.Logon正在向服务器发出Post请求

我要测试的是调用accountFactory.Logon时的情况:

  • 成功时,调用window.location.href
  • 错误
    $scope.hasServerError
    设置为true
  • 到目前为止,我已经做到了:

    "use strict";
    
    describe("Logon Controller", function () {
    
        var $scope, $location, $rootScope, $httpBackend, $controller, $window, createController;
    
        beforeEach(function () {
            module("logonController");
        });
    
        beforeEach(inject(function ($injector) {
            $rootScope = $injector.get("$rootScope");
            $scope = $rootScope.$new();
            $location = $injector.get("$location");
            $httpBackend = $injector.get("$httpBackend");
            $controller = $injector.get("$controller");
            $window = $injector.get("$window");
        }));
    
        beforeEach(function () {
            createController = function () {
                return $controller("logonController", {
                    "$scope": $scope,
                });
            };
    
            $scope.data = {
                LogOnModel: { username: "user", password: "pass" }
            };
    
            $window = { location: { href: jasmine.createSpy() } };
        });
    
        it("should redirect on successfull login", function () {
            var controller = createController();
    
            $httpBackend.whenPOST("/Account/Logon").respond(function (method, url, data, headers) {
                return [200, {}, {}];
            });
    
            $scope.Logon();
    
            $httpBackend.flush();
    
            expect($window.location.href).toHaveBeenCalled();
        });
    });
    
    我的想法是在$window.location.href上创建一个间谍,只检查它是否被调用。但是我越来越

    预期已调用未知间谍


    正如我所说,我对测试javascript非常陌生,因此我们非常感谢您的帮助。

    您需要创建一个间谍:

    spyOn($window, 'location.href').andCallThrough();
    
    但更重要的是,您不应该在控制器测试中测试服务的功能

    Sten Muchow的回答在几个方面是错误的:
  • 不能将复合属性名(“location.href”)指定为spyOn的第二个参数。您必须提供一个不动产名称
  • 即使您正确地使用spyOn,andCallThrough()仍会引发异常,因为$window.location.href不是一个可以通过调用的函数
  • 但他仍然正确地指出,您不应该将控制器测试和服务测试混为一谈

    要回答这个问题: 您的期望没有得到满足(甚至间谍仍然存在*)的原因是,您正在promise的then()函数中执行$window.location.href赋值。这意味着,它将异步执行,即在expect()调用之后。为了解决这个问题,您需要使您的测试异步工作(关于如何做到这一点,我建议您参阅Jasmine文档:)

    *在accountFactory.Logon中,通过执行
    $window.location.href=
    (即分配),您将有效地覆盖spy

    更好的解决方案: 您应该使用
    $location.url()
    ,而不是操纵
    $window.location.href

    $location
    是一种角度核心服务。您将受益于Angular应用程序生命周期内的集成(即,当url更改时,将自动处理观察者)+它与现有HTML5API(如历史API)无缝集成

    然后,您可以监视$location.url(),就像监视$window.location.href(如果它是一个函数)一样