2013-02-24 84 views
22

मैं इकाई परीक्षण करने के लिए चमेली उपयोग कर रहा हूँ एक AngularJS नियंत्रक है कि एक सेवा विधि है कि एक वादा ऑब्जेक्ट बुला के परिणाम के दायरे पर एक चर सेट में हल नहीं किया जा रहा वादा सेवा:AngularJS इकाई परीक्षण

function getStuff() { 
    return $http.get('api/stuff').then(function (httpResult) { 
     return httpResult.data; 
    }); 
} 

यह मेरे कोणीय अनुप्रयोग के संदर्भ में ठीक काम करता है, लेकिन जैस्मीन यूनिट परीक्षण में काम नहीं करता है। मैंने पुष्टि की है कि "फिर" कॉलबैक यूनिट परीक्षण में निष्पादित हो रहा है, लेकिन $ scope.myVar वादा कभी भी कॉलबैक के वापसी मूल्य पर सेट नहीं हो जाता है।

मेरे इकाई परीक्षण:

describe('My Controller', function() { 
    var scope; 
    var serviceMock; 
    var controller; 
    var httpBackend; 

    beforeEach(inject(function ($rootScope, $controller, $httpBackend, $http) { 
    scope = $rootScope.$new(); 
    httpBackend = $httpBackend; 
    serviceMock = { 
     stuffArray: [{ 
     FirstName: "Robby" 
     }], 

     getStuff: function() { 
     return $http.get('api/stuff').then(function (httpResult) { 
      return httpResult.data; 
     }); 
     } 
    }; 
    $httpBackend.whenGET('api/stuff').respond(serviceMock.stuffArray); 
    controller = $controller(MyController, { 
     $scope: scope, 
     service: serviceMock 
    }); 
    })); 

    it('should set myVar to the resolved promise value', 
    function() { 
     httpBackend.flush(); 
     scope.$root.$digest(); 
     expect(scope.myVar[0].FirstName).toEqual("Robby"); 
    }); 
}); 

इसके अलावा, अगर मैं इकाई परीक्षण निम्न के नियंत्रक बदलने से गुजरता है:

var MyController = function($scope, service) { 
    service.getStuff().then(function(result) { 
     $scope.myVar = result; 
    }); 
} 

है क्यों वादा कॉलबैक परिणाम मूल्य $ गुंजाइश के लिए प्रचारित नहीं किया जा रहा यूनिट परीक्षण में .myVar? पूर्ण कार्य कोड http://jsfiddle.net/s7PGg/5/

उत्तर

21

मुझे लगता है कि इस "रहस्य" की कुंजी यह तथ्य है कि एंगुलरजेएस स्वचालित रूप से वादे को हल करेगा (और परिणाम प्रस्तुत करेगा) यदि टेम्पलेट में इंटरपोलेशन निर्देश में उपयोग किया जाता है तो निम्नलिखित jsfiddle देखें।

MyCtrl = function($scope, $http) { 
    $scope.promise = $http.get('myurl', {..}); 
} 

और टेम्पलेट:: क्या मेरा मतलब है कि इस नियंत्रक दिया है

<span>{{promise}}</span> 

AngularJS, $ http कॉल पूरा होने पर, "देख" होगा कि एक वादा हल किया गया था और टेम्पलेट फिर से प्रस्तुत करना होगा हल किए गए परिणामों के साथ।

$ q वादों कोणीय में templating इंजन, द्वारा मान्यता प्राप्त हैं जो मतलब यह है कि टेम्पलेट्स में आप के रूप में एक गुंजाइश से जुड़ी वादों का इलाज कर सकते हैं यदि वे जिसके परिणामस्वरूप मूल्यों थे: यह क्या अस्पष्ट $q documentation में बताया गया है है ।

वह कोड जहां यह जादू होता है here देखा जा सकता है।

लेकिन, यह "जादू" तब होता है जब कोई टेम्पलेट होता है ($parse सेवा, अधिक सटीक होने के लिए)। आपके यूनिट परीक्षण में कोई टेम्पलेट शामिल नहीं है इसलिए वादा संकल्प स्वचालित रूप से प्रचारित नहीं होता है।

अब, मुझे कहना होगा कि यह स्वचालित संकल्प/परिणाम प्रचार बहुत सुविधाजनक है लेकिन भ्रमित हो सकता है, क्योंकि हम इस प्रश्न से देख सकते हैं। यही कारण है कि मैं स्पष्ट रूप से संकल्प परिणाम प्रचार करने के लिए के रूप में तुमने किया था पसंद करते हैं:

var MyController = function($scope, service) { 
    service.getStuff().then(function(result) { 
     $scope.myVar = result; 
    }); 
} 
+0

महान जवाब, मैं डॉक्स में है कि थोड़ा चूक लग रहा था। – robbymurphy

+0

यदि आप मेरे जैसे बैक एंड का मज़ाक उड़ा रहे हैं, तो परिणाम वास्तविक प्रतिक्रिया सामग्री वाले "डेटा" संपत्ति के साथ एक समग्र होगा। – Gepsens

+3

कोणीय 1.2 में, वादे अब स्वचालित रूप से हल नहीं किए जाते हैं (AKA, unwrapped)। – zhon

3

@ pkozlowski.opensource जवाब क्यों (धन्यवाद!), लेकिन परीक्षण में इसके चारों ओर पाने के लिए नहीं।

जिस समाधान पर मैं अभी पहुंचा हूं वह यह जांचने के लिए है कि HTTP में सेवा में कॉल किया जा रहा है, और फिर नियंत्रक परीक्षणों में सेवा विधियों पर जासूसी और वादे के बजाय वास्तविक मूल्यों को वापस कर दें।

var services = angular.module('app.services', []); 

services.factory('User', function ($q, $http) { 

    function GET(path) { 
    var defer = $q.defer(); 
    $http.get(path).success(function (data) { 
     defer.resolve(data); 
    } 
    return defer.promise; 
    } 

    return { 
    get: function (handle) { 
     return GET('/api/' + handle); // RETURNS A PROMISE 
    }, 

    // ... 

    }; 
}); 

परीक्षण सेवा, हम परवाह नहीं है कि क्या, लौटे मूल्यों के लिए होता ही नहीं HTTP कॉल सही ढंग से किए गए:

मान लीजिए हम एक उपयोगकर्ता सेवा है कि हमारे सर्वर से बात करती है।

describe 'User service', -> 
    User = undefined 
    $httpBackend = undefined 

    beforeEach module 'app.services' 

    beforeEach inject ($injector) -> 
    User = $injector.get 'User' 
    $httpBackend = $injector.get '$httpBackend' 

    afterEach -> 
    $httpBackend.verifyNoOutstandingExpectation() 
    $httpBackend.verifyNoOutstandingRequest()   

    it 'should get a user', -> 
    $httpBackend.expectGET('/api/alice').respond { handle: 'alice' } 
    User.get 'alice' 
    $httpBackend.flush()  

अब हमारे नियंत्रक परीक्षणों में, HTTP के बारे में चिंता करने की आवश्यकता नहीं है। हम केवल यह देखना चाहते हैं कि उपयोगकर्ता सेवा को काम पर रखा जा रहा है।

angular.module('app.controllers') 
    .controller('UserCtrl', function ($scope, $routeParams, User) { 
    $scope.user = User.get($routeParams.handle); 
    }); 

इसका परीक्षण करने के लिए, हम उपयोगकर्ता सेवा पर जासूसी करते हैं।

describe 'UserCtrl',() -> 

    User = undefined 
    scope = undefined 
    user = { handle: 'charlie', name: 'Charlie', email: '[email protected]' } 

    beforeEach module 'app.controllers' 

    beforeEach inject ($injector) -> 
    # Spy on the user service 
    User = $injector.get 'User' 
    spyOn(User, 'get').andCallFake -> user 

    # Other service dependencies 
    $controller = $injector.get '$controller' 
    $routeParams = $injector.get '$routeParams' 
    $rootScope = $injector.get '$rootScope' 
    scope = $rootScope.$new(); 

    # Set up the controller 
    $routeParams.handle = user.handle 
    UserCtrl = $controller 'UserCtrl', $scope: scope 

    it 'should get the user by :handle', -> 
    expect(User.get).toHaveBeenCalledWith 'charlie' 
    expect(scope.user.handle).toBe 'charlie'; 

वादे को हल करने की कोई आवश्यकता नहीं है। उम्मीद है की यह मदद करेगा।

+0

उस उदाहरण में जहां आप सेवा का परीक्षण कर रहे हैं, हमें $ httpBackend.flush(): $ रूटस्कोप कॉल करने से पहले निम्नलिखित जोड़ना होगा। $ लागू करें() – blaster

+2

@blaster ... तो आप कुछ गलत कर रहे हैं। नियंत्रकों और दायरे के संदर्भ के बाहर सेवाओं का परीक्षण किया जाना चाहिए। –

8

मुझे एक ही समस्या थी और मेरे नियंत्रक को $ scope.myVar को सीधे वादे को सौंप दिया गया। फिर परीक्षण में, मैंने एक और वादे पर आरोप लगाया कि जब यह हल हो जाता है तो वादे के अपेक्षित मूल्य पर जोर दिया जाता है।

var expectPromisedValue = function(promise, expectedValue) { 
    promise.then(function(resolvedValue) { 
    expect(resolvedValue).toEqual(expectedValue); 
    }); 
} 

नोट है कि जब आप expectPromisedValue फोन और जब वादा परीक्षण के अंतर्गत अपने कोड से हल हो गई है के आदेश के आधार पर, आप मैन्युअल रूप से अंतिम चक्र को पचाने को गति प्रदान करने की आवश्यकता हो सकती: मैं इस तरह एक सहायक विधि का इस्तेमाल किया इन्हें() विधियों को आग लगाने के लिए चलाने के लिए - इसके बिना आपका परीक्षण resolvedValueexpectedValue के बराबर या नहीं, इस पर ध्यान दिए बिना हो सकता है।

सुरक्षा के लिए, एक afterEach() कॉल में ट्रिगर रखें ताकि आप हर परीक्षा के लिए यह याद करने की जरूरत नहीं:

afterEach(inject(function($rootScope) { 
    $rootScope.$apply(); 
}));