0

我有一个控制器调用一个服务,它是一个资源的包装。像这样:测试控制器,取决于基于资源的服务

app.factory("Service", ["$resource", function ($resource) { 
      return $resource("/api/Service/get"); 
     }]); 

服务方法的返回值被分配给控制器内的变量。通常,该变量的类型为Resource,它包含promise。当promise被解析时,变量将被填充所有从后端返回的值。 我跟踪承诺上的,然后,以便修改从后端接收到的模型。像这样:

this.model = Service.get(); 
    this.model.$promise.then(function(data) { 
     // do something with data 
    }); 

我需要测试我控制器的结果模型变量的值。 我发现要做到这一点的唯一方法,就是使用$ httpBackend以真正实现我的服务。然而,这是丑陋的,因为然后,测试我的控制器,我必须通过请求路径"api/Service/get"httpBackend.when()为了使它以某种价值回应。

摘录形成我的测试:

// call Controller 
$httpBackend.when('GET', '/api/Service/get').respond(someData); 
$httpBackend.flush(); 
expect(scope.model.property).toBe(null); 

这似乎和感觉完全错误的。使用单独的服务来处理资源的关键在于控制器不知道url和http方法名称。所以我该怎么做?

换句话说,我想测试的是then被调用,并做我需要做的事情。

我想我可以创建一个单独的服务,在then中调用,做我需要做的模型,但它感觉有点矫枉过正,如果我想要做的是例如将一个字段设置为null取决于一个简单的条件。

+0

这篇对你的工作? – tasseKATT

回答

0

你是对的,你不应该使用$httpBackend,除非你在你正在测试的控制器中使用$http

正如您所写,控制器不需要知道关于Service实施的任何信息。控制器知道的是Service有一个get方法,该方法返回一个具有$promise属性(承诺)的对象。

你想要做的是在你的测试中使用Service的假实现。有多种方式可以通过mock,间谍,存根等来实现,具体取决于你的用例以及你正在使用的测试框架。

一种方法是创建一个假的实现是这样的:

var Service = { 
    get: function() { 

    deferred = $q.defer(); 

    return { 
     $promise: deferred.promise 
    }; 
    } 
}; 

你要能够从测试访问deferred,所以你可以根据你要测试的resolvereject承诺。

完全安装:

var $rootScope, 
    scope, 
    createController, 
    $q, 
    deferred; 

var Service = { 
    get: function() { 

    deferred = $q.defer(); 

    return { 
     $promise: deferred.promise 
    }; 
    } 
}; 

beforeEach(function() { 

    module('App'); 

    inject(function(_$rootScope_, $controller, _$q_) { 

    $rootScope = _$rootScope_; 

    scope = $rootScope.$new(); 

    createController = function() { 
     $controller('MyController', { 
     $scope: scope, 
     Service: Service 
     }); 
    }; 

    $q = _$q_; 
    }); 
}); 

控制器实现:

app.controller('MyController', function($scope, Service) { 

    $scope.property = false; 

    $scope.model = Service.get(); 

    $scope.model.$promise.then(function(data) { 

    if (data) { 
     $scope.property = true; 
    } 
    }); 
}); 

然后,您可以窥探假执行断言它是正确调用。

例茉莉:

spyOn(Service, 'get').and.callThrough(); 

您需要and.callThrough()或通话将被中断,你的假的实施将不会被使用。

您现在已经通过手动创建控制器,解决承诺,引发消化循环,可以测试不同状态的完全控制:

it('Should work', function() { 

    spyOn(Service, 'get').and.callThrough(); 

    expect(Service.get).not.toHaveBeenCalled(); 

    createController(); 

    expect(Service.get).toHaveBeenCalled(); 

    expect(scope.property).toBeFalsy(); 

    deferred.resolve('some data'); 
    $rootScope.$digest(); 

    expect(scope.property).toBeTruthy(); 
}); 

演示:http://plnkr.co/edit/th2pLWdVa8AZWOyecWOF?p=preview