在AngularJS中测试控制器时,如何测试服务回调中的数据集?

在AngularJS中测试控制器时,如何测试服务回调中的数据集?,angularjs,unit-testing,jasmine,Angularjs,Unit Testing,Jasmine,假设我有一个查询一些数据并在控制器中设置的服务,有点类似于: (Method on controller) DogService.query(function(data)){ if(data.isSuccess){ $scope.IloveDogs = true; $scope.dogLovers += 1; } }) 它是高度简化的,但在我的控制器中,我如何测试调用模拟dogService时,它是否设置了正确的数据 如果为了简单起见,我们说函数不是异步的,并且处理承

假设我有一个查询一些数据并在控制器中设置的服务,有点类似于:

(Method on controller)

DogService.query(function(data)){
  if(data.isSuccess){
    $scope.IloveDogs = true;
    $scope.dogLovers += 1;
  }
})
它是高度简化的,但在我的控制器中,我如何测试调用模拟dogService时,它是否设置了正确的数据

如果为了简单起见,我们说函数不是异步的,并且处理承诺,那么我将创建一个mock并将其注入控制器。模拟可能看起来像:

var DogService = {
  query: function(){
    return true;
  }
}
不幸的是,这没有运行$scope.IloveDogs设置为true的代码,而dogLovers则递增1


有什么想法吗?因为我不想把控制器中的代码从服务复制到模拟服务?

这是我通常在单元测试中模拟服务的方式。 您没有提到您使用的测试框架,所以我假设Jasmine是目前最流行的测试框架

我只是创建一个哑对象作为我的模拟对象,然后只是Jasmine的内置间谍功能来指定它返回的内容。注意,这是Jasmine 2.0的语法

我使用$q创建一个承诺,并确保我能够从测试中引用它,以便解决它

describe('Spec', function() {

    var scope;
    var catServiceMock;

    var deferredCatCall;

    beforeEach(module('myModule'));

    beforeEach(inject(function($controller, $rootScope, $q) {
        scope = $rootScope;

        //Create a mock and spy on it to return a promise
        deferredCatCall = $q.defer();
        catServiceMock = {
            query: function() {}
        };
        spyOn(catServiceMock, 'query').and.returnValue(deferredCatCall.promise);

        //Inject the mock into the controller
        $controller('MyCtrl', {
            $scope: scope,
            catService: catServiceMock
        });
    }));

    it('proves that cats are better than dogs', function() {
        //resolve the promise that was returned by the mock
        deferredCatCall.resolve({
            isSuccess: true
        });
        //Need to trigger a $digest loop so angular process the resolved promise
        scope.$digest();

        //Check that the controller callback did something
        expect(scope.iLoveCats).toBeTruthy();
    });

});
对于不使用承诺的服务,我可能会这样做:

describe('Spec', function() {

    var scope;
    var catServiceMock;

    beforeEach(module('myModule'));

    beforeEach(inject(function($controller, $rootScope, $q) {
        scope = $rootScope;

        //Create a mock and spy on it to return a value
        catServiceMock = {
            query: function() {}
        };
        spyOn(catServiceMock, 'query').and.returnValue({
            isSuccess: true
        });

        //Inject the mock into the controller
        $controller('MyCtrl', {
            $scope: scope,
            catService: catServiceMock
        });
    }));

    it('proves that cats are better than dogs', function() {
        //Check that the controller callback did something
        expect(scope.iLoveCats).toBeTruthy();
    });

});
这种方法的主要问题是,在实例化控制器之前,您必须指定服务将返回什么。这意味着,如果您想测试控制器对从服务接收到的不同数据的行为,您必须将多个beforeach块嵌套在不同的descripe块中,虽然乍一看它似乎不是测试中的样板,但最终您将得到更多


这就是为什么我更喜欢我的服务返回承诺的原因之一,即使它们不是异步的。

您关于jasmin的假设是正确的,请参阅标签;。如果你实际上没有异步方法,你会怎么做呢?我现在也要看看标签了我的所有服务都使用承诺,但我将尝试想出一个如何测试非承诺服务的示例。@Dofs我已经编辑过,我将为非承诺返回服务做些什么。并且还解释了为什么即使不是异步操作,我也会让服务返回承诺;它似乎没有进入信息回调,我没有使用jasmin 2.0,而是使用andReturn…我没有使用Angle resource,所以我不确定它到底是如何工作的。我甚至不知道$resource函数返回什么,所以我不确定我能帮上什么忙。我发布的代码片段只是我在一般情况下的做法,我不确定在混合中引入$resource时需要做哪些修改。这并不是说它不能完成,我只是对那个模块了解不够。