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