web-dev-qa-db-fra.com

Injection de services dépendants lors du test d'unité des services AngularJS

Je teste le service A, mais le service A dépend du service B (c'est-à-dire que le service B est injecté dans le service A).

J'ai vu cette question mais mon cas est un peu différent car à mon avis, il est plus logique de se moquer du service B au lieu d'injecter une instance réelle de service B. Je me moquerais avec un espion au jasmin.

Voici un exemple de test:

describe("Sample Test Suite", function() {

  beforeEach(function() {

    module('moduleThatContainsServiceA');

    inject([
      'serviceA', function(service) {
        this.service = service;
      }
    ]);

  });

  it('can create an instance of the service', function() {
    expect(this.service).toBeDefined();
  });
});

L'erreur que j'obtiens est:

Erreur: fournisseur inconnu: serviceBProvider

Comment pourrais-je faire quelque chose comme ça?

51
Roy Truelove

En fait, dans AngularJS Dependency Injection utilise la règle des "derniers gains". Vous pouvez donc définir votre service dans votre test juste après avoir inclus votre module et vos dépendances, puis lorsque le service A que vous testez demandera le service B à l'aide de DI, AngularJS donnera une version simulée du service B.

Cela se fait souvent en définissant un nouveau module comme MyAppMocks, en y plaçant des services/valeurs simulés et en ajoutant simplement ce module comme dépendance.

Type de (schématiquement):

beforeEach(function() {
  angular.module('MyAppMocks',[]).service('B', ...));
  angular.module('Test',['MyApp','MyAppMocks']);
  ...
45
Valentyn Shybanov

Je faisais cela dans CoffeeScript et j'ai trouvé un problème supplémentaire. (De plus, j'ai trouvé que le code sur cette page était laconique.) Voici un exemple de travail complet:

describe 'serviceA', ->
   mockServiceB = {}

   beforeEach module 'myApp' # (or just 'myApp.services')

   beforeEach ->
      angular.mock.module ($provide) ->
         $provide.value 'serviceB', mockServiceB
         null

   serviceA = null
   beforeEach inject ($injector) ->
      serviceA = $injector.get 'serviceA'

   it 'should work', ->
      expect( true ).toBe( true )
      #serviceA.doStuff()

Sans retourner explicitement null après $provide.value, Je continuais à recevoir Error: Argument 'fn' is not a function, got Object. J'ai trouvé la réponse dans ce fil Google Groupes .

24
jab

La solution Valentyn a fonctionné pour moi, mais il existe une autre alternative.

beforeEach(function () {

    angular.mock.module("moduleThatContainsServiceA", function ($provide) {
                $provide.value('B', ...);
            });
});

Ensuite, lorsque le service AngularJS A demande le service B par injection de dépendance, votre maquette du service B sera fournie à la place du service B du moduleThatContainsServiceA.

De cette façon, vous n'avez pas besoin de créer un module angular angulaire juste pour se moquer d'un service.

20
Richard Keller

Je trouve que la méthode la plus simple consiste simplement à injecter le service B et à s'en moquer. par exemple. La voiture de service dépend du moteur de service. Maintenant, nous devons nous moquer du moteur lors du test de la voiture:

describe('Testing a car', function() {
      var testEngine;

  beforeEach(module('plunker'));
  beforeEach(inject(function(engine){
    testEngine = engine;
  }));

  it('should drive slow with a slow engine', inject(function(car) {
    spyOn(testEngine, 'speed').andReturn('slow');
    expect(car.drive()).toEqual('Driving: slow');
  }));
});

Référence: https://github.com/angular/angular.js/issues/1635

6
gm2008

C'est ce qui a fonctionné pour moi. La clé est de définir un vrai module à moquer. Appeler angular.mock.module rend le vrai module moquable et permet de connecter les choses.

    beforeEach( ->
        @weather_service_url = '/weather_service_url'
        @weather_provider_url = '/weather_provider_url'
        @weather_provider_image = "test.jpeg"
        @http_ret = 'http_works'
        module = angular.module('mockModule',[])
        module.value('weather_service_url', @weather_service_url)
        module.value('weather_provider_url', @weather_provider_url)
        module.value('weather_provider_image', @weather_provider_image)
        module.service('weather_bug_service', services.WeatherBugService)

        angular.mock.module('mockModule')

        inject( ($httpBackend,weather_bug_service) =>
            @$httpBackend = $httpBackend
            @$httpBackend.when('GET', @weather_service_url).respond(@http_ret)
            @subject = weather_bug_service
        )
    )
1
Nick