एक AngularJS सेवा में एक मॉक इंजेक्षन


114

मेरे पास एक AngularJS सेवा लिखी गई है और मैं इसका परीक्षण करना चाहूंगा।

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) {

    this.something = function() {
        // Do something with the injected services
    };

    return this;
});

मेरी app.js फ़ाइल में ये पंजीकृत हैं:

angular
.module('myApp', ['fooServiceProvider','barServiceProvider','myServiceProvider']
)

मैं परीक्षण कर सकता हूं कि DI इस प्रकार काम कर रहा है:

describe("Using the DI framework", function() {
    beforeEach(module('fooServiceProvider'));
    beforeEach(module('barServiceProvider'));
    beforeEach(module('myServiceProvder'));

    var service;

    beforeEach(inject(function(fooService, barService, myService) {
        service=myService;
    }));

    it("can be instantiated", function() {
        expect(service).not.toBeNull();
    });
});

यह साबित हुआ कि सेवा को डीआई फ्रेमवर्क द्वारा बनाया जा सकता है, हालांकि इसके बाद मैं यूनिट को परीक्षण करना चाहता हूं, जिसका अर्थ है कि इंजेक्शन वाली वस्तुओं का मजाक उड़ाना।

मैं यह कार्य कैसे करूं?

मैंने अपने मॉक ऑब्जेक्ट्स को मॉड्यूल में डालने की कोशिश की है, जैसे

beforeEach(module(mockNavigationService));

और सेवा की परिभाषा को फिर से लिखना:

function MyService(http, fooService, barService) {
    this.somthing = function() {
        // Do something with the injected services
    };
});

angular.module('myServiceProvider', ['fooServiceProvider', 'barServiceProvider']).
    factory('myService', function ($http, fooService, barService) { return new MyService($http, fooService, barService); })

लेकिन उत्तरार्द्ध सभी के रूप में DI द्वारा बनाई जा रही सेवा को रोकने के लिए लगता है।

क्या किसी को पता है कि मैं अपनी यूनिट परीक्षणों के लिए इंजेक्शन सेवाओं का मजाक कैसे उड़ा सकता हूं?

धन्यवाद

डेविड


आप मेरे इस जवाब पर एक और सवाल उठा सकते हैं, मुझे उम्मीद है कि यह आपके लिए मददगार हो सकता है।
रिमिगियो

इसके अलावा stackoverflow.com/questions/14238490
jab

जवाबों:


183

आप का उपयोग करके अपनी सेवा में नकली इंजेक्शन लगा सकते हैं $provide

यदि आपके पास निर्भरता के साथ निम्नलिखित सेवा है, जिसमें getSomething नामक एक विधि है:

angular.module('myModule', [])
  .factory('myService', function (myDependency) {
        return {
            useDependency: function () {
                return myDependency.getSomething();
            }
        };
  });

आप निम्न के रूप में myD निर्भरता के एक नकली संस्करण को इंजेक्ट कर सकते हैं:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(function () {

      mockDependency = {
          getSomething: function () {
              return 'mockReturnValue';
          }
      };

      module(function ($provide) {
          $provide.value('myDependency', mockDependency);
      });

  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

ध्यान दें कि कॉल के कारण $provide.valueआपको वास्तव में कहीं भी स्पष्ट रूप से myD dependency को इंजेक्ट करने की आवश्यकता नहीं है। यह myService के इंजेक्शन के दौरान हुड के नीचे होता है। जब यहां मॉकडिपेंडेंसी स्थापित की जाती है, तो यह आसानी से एक जासूस के रूप में हो सकता है।

उस महान वीडियो के लिंक के लिए वफादारी के लिए धन्यवाद ।


13
महान काम करता है, लेकिन एक विस्तार से सावधान रहें: beforeEach(module('myModule'));कॉल से पहले आने के लिए एचएएस है beforeEach(function () { MOCKING }), या फिर नकली वास्तविक सेवाओं द्वारा अधिलेखित हो जाएगा!
निकोस परसकेवोपोलोस

1
क्या सेवा करने का नहीं बल्कि उसी तरीके से स्थिर रहने का एक तरीका है?
आर्टेम

5
निकोस की टिप्पणी के समान, किसी भी $provideकॉल का उपयोग करने से पहले किया जाना चाहिए $injectorअन्यथा, आपको एक त्रुटि प्राप्त होगी:Injector already created, can not register a module!
प्रोवेंसमैक

7
यदि आपके नकली को $ q की आवश्यकता हो तो क्या होगा? फिर आप मॉक रजिस्टर करने के लिए कॉलिंग मॉड्यूल () से पहले नकली में $ q इंजेक्ट नहीं कर सकते। कोई विचार?
जेक

4
यदि आप कॉफ़ीस्क्रिप्ट का उपयोग कर रहे हैं और आप देख रहे हैं Error: [ng:areq] Argument 'fn' is not a function, got Object, तो इसके returnबाद लाइन पर रखना सुनिश्चित करें $provide.value(...)। अकस्मात लौटने $provide.value(...)से मेरे लिए वह त्रुटि हुई।
यंडोलोक

4

जिस तरह से मैं इसे देखता हूं, खुद सेवाओं का मजाक उड़ाने की कोई जरूरत नहीं है। बस सेवा पर कार्यों का मजाक उड़ाते हैं। इस तरह, आप कोणीय अपनी वास्तविक सेवाओं को इंजेक्ट कर सकते हैं जैसा कि यह पूरे ऐप में है। फिर, जैस्मीन के spyOnफ़ंक्शन का उपयोग करते हुए सेवा पर कार्यों का मजाक उड़ाएं ।

अब, यदि सेवा स्वयं एक फ़ंक्शन है, और ऐसी वस्तु नहीं है जिसका आप उपयोग कर सकते हैं spyOn, तो इसके बारे में जाने का एक और तरीका है। मुझे ऐसा करने की ज़रूरत थी, और मुझे कुछ ऐसा मिला जो मेरे लिए बहुत अच्छा काम करता है। देखें कि आप कोणीय सेवा का मखौल कैसे उड़ाते हैं?


3
मुझे नहीं लगता कि यह सवाल का जवाब देता है। क्या होगा अगर सेवा की फैक्ट्री का मजाक उड़ाया जाए, जो कुछ गैर-तुच्छ है, जैसे डेटा के लिए सर्वर को हिट करना? यह एक अच्छा कारण होगा कि वह इसका मजाक उड़ाए। आप सर्वर कॉल से बचना चाहते हैं, और इसके बजाय नकली डेटा के साथ सेवा का नकली संस्करण बनाते हैं। $ $ Mocking एक अच्छा समाधान भी नहीं है, क्योंकि तब आप वास्तव में एक परीक्षण में दो सेवाओं का परीक्षण कर रहे हैं, यूनिट-परीक्षण के बजाय अलगाव में दो सेवाओं का। इसलिए मैं इस सवाल पर फिर से विचार करना चाहूंगा। एक यूनिट टेस्ट में आप किसी अन्य सेवा के लिए एक नकली सेवा कैसे पास करते हैं?
पैट्रिक अर्नेसेन

1
यदि आप डेटा के लिए सर्वर को हिट करने वाली सेवा के बारे में चिंतित हैं, तो यह $ httpBackend ( docs.angularjs.org/api/ngMock.$httpBackend ) के लिए क्या है । मुझे यकीन नहीं है कि सेवा के कारखाने में और क्या चिंता होगी जो पूरी सेवा का मजाक उड़ाएगी।
dnc253

2

कोकुलर और जैस्मीन में मॉकिंग निर्भरता को आसान बनाने में मदद करने के लिए एक और विकल्प क्विकमॉक का उपयोग करना है। यह GitHub पर पाया जा सकता है और आपको पुन: प्रयोज्य तरीके से सरल मोक्स बनाने की अनुमति देता है। आप इसे नीचे दिए गए लिंक के माध्यम से GitHub से क्लोन कर सकते हैं। README बहुत ही आत्म व्याख्यात्मक है, लेकिन उम्मीद है कि यह भविष्य में दूसरों की मदद कर सकता है।

https://github.com/tennisgent/QuickMock

describe('NotificationService', function () {
    var notificationService;

    beforeEach(function(){
        notificationService = QuickMock({
            providerName: 'NotificationService', // the provider we wish to test
            moduleName: 'QuickMockDemo',         // the module that contains our provider
            mockModules: ['QuickMockDemoMocks']  // module(s) that contains mocks for our provider's dependencies
        });
    });
    ....

यह स्वचालित रूप से ऊपर उल्लिखित सभी बॉयलरप्लेट का प्रबंधन करता है, इसलिए आपको हर परीक्षण में उस नकली इंजेक्शन कोड को लिखने की ज़रूरत नहीं है। उम्मीद है की वो मदद करदे।


2

जॉन गैलाम्बोस के जवाब के अलावा : यदि आप किसी सेवा के विशिष्ट तरीकों का मजाक बनाना चाहते हैं, तो आप इसे इस तरह से कर सकते हैं:

describe('Service: myService', function () {

  var mockDependency;

  beforeEach(module('myModule'));

  beforeEach(module(function ($provide, myDependencyProvider) {
      // Get an instance of the real service, then modify specific functions
      mockDependency = myDependencyProvider.$get();
      mockDependency.getSomething = function() { return 'mockReturnValue'; };
      $provide.value('myDependency', mockDependency);
  });

  it('should return value from mock dependency', inject(function (myService) {
      expect(myService.useDependency()).toBe('mockReturnValue');
  }));

});

1

यदि आपका नियंत्रक इस तरह एक निर्भरता में लेने के लिए लिखा है:

app.controller("SomeController", ["$scope", "someDependency", function ($scope, someDependency) {
    someDependency.someFunction();
}]);

तो आप someDependencyइस तरह एक जैस्मीन परीक्षण में एक नकली बना सकते हैं :

describe("Some Controller", function () {

    beforeEach(module("app"));


    it("should call someMethod on someDependency", inject(function ($rootScope, $controller) {
        // make a fake SomeDependency object
        var someDependency = {
            someFunction: function () { }
        };

        spyOn(someDependency, "someFunction");

        // this instantiates SomeController, using the passed in object to resolve dependencies
        controller("SomeController", { $scope: scope, someDependency: someDependency });

        expect(someDependency.someFunction).toHaveBeenCalled();
    }));
});

9
सवाल सेवाओं के बारे में है, जो टेस्ट कंट्रोलर में $ कंट्रोलर के रूप में किसी भी समकक्ष सेवा के साथ तत्काल नहीं मिलता है। दूसरे शब्दों में, एक निर्भरता में गुजरते हुए, पहले वाले ब्लॉक में $ सेवा () को कॉल नहीं करता है।
मॉरिस सिंगर

1

मैंने हाल ही में ngImprovedTesting जारी किया है जो कि AngularJS की राह में आसान परीक्षण करना चाहिए।

'MyService' ("myApp" मॉड्यूल से) का परीक्षण करने के लिए, इसके fooService और बारसर्फ़ पर निर्भरता के कारण जो आपको सरल लगता है, वह आपके जैस्मिन परीक्षण में निम्न कार्य कर सकता है:

beforeEach(ModuleBuilder
    .forModule('myApp')
    .serviceWithMocksFor('myService', 'fooService', 'barService')
    .build());

NgImprovedTesting के बारे में अधिक जानकारी के लिए इसकी परिचयात्मक ब्लॉग पोस्ट देखें: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/


1
यह डाउन-वोट क्यों किया गया? मैं एक टिप्पणी के बिना नीचे मतदान में मूल्य समझ में नहीं आता।
याकूब ब्रेवर

0

मुझे पता है कि यह पुराना सवाल है, लेकिन एक और आसान तरीका है, आप मॉक बना सकते हैं और एक समारोह में मूल इंजेक्शन को निष्क्रिय कर सकते हैं, यह सभी तरीकों पर स्पाईऑन का उपयोग करके किया जा सकता है। नीचे कोड देखें।

var mockInjectedProvider;

    beforeEach(function () {
        module('myModule');
    });

    beforeEach(inject(function (_injected_) { 
      mockInjectedProvider  = mock(_injected_);
    });

    beforeEach(inject(function (_base_) {
        baseProvider = _base_;
    }));

    it("injectedProvider should be mocked", function () {
    mockInjectedProvider.myFunc.andReturn('testvalue');    
    var resultFromMockedProvider = baseProvider.executeMyFuncFromInjected();
        expect(resultFromMockedProvider).toEqual('testvalue');
    }); 

    //mock all service methods
    function mock(angularServiceToMock) {

     for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
      spyOn(angularServiceToMock,Object.getOwnPropertyNames(angularServiceToMock)[i]);
     }
                return angularServiceToMock;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.