javascript - tutorial - mock injector angular




Injetando uma simulação em um serviço AngularJS (5)

Eu tenho um serviço AngularJS escrito e gostaria de testá-lo em unidade.

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

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

    return this;
});

Meu arquivo app.js tem estes registrados:

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

Eu posso testar o DI está funcionando como tal:

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();
    });
});

Isso provou que o serviço pode ser criado pelo framework DI, no entanto, eu quero testar o serviço em unidade, o que significa zombar dos objetos injetados.

Como faço para fazer isso?

Eu tentei colocar meus objetos de simulação no módulo, por exemplo

beforeEach(module(mockNavigationService));

e reescrevendo a definição de serviço como:

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); })

Mas o último parece impedir que o serviço seja criado pelo DI como um todo.

Alguém sabe como posso zombar dos serviços injetados para meus testes de unidade?

obrigado

David


Além da resposta de John Galambos : se você quer apenas ridicularizar métodos específicos de um serviço, você pode fazer assim:

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');
  }));

});

Do jeito que eu vejo, não há necessidade de zombar dos serviços em si. Simplesmente zombar das funções no serviço. Dessa forma, você pode ter injeção angular em seus serviços reais, como acontece em todo o aplicativo. Em seguida, zombe das funções no serviço, conforme necessário, usando a função spyOn do Jasmine.

Agora, se o serviço em si é uma função, e não um objeto com o qual você pode usar o spyOn , há outra maneira de fazer isso. Eu precisava fazer isso e encontrei algo que funciona muito bem para mim. Veja Como você zomba do serviço Angular que é uma função?


Outra opção para facilitar as dependências de simulação em Angular e Jasmine é usar o QuickMock. Ele pode ser encontrado no GitHub e permite que você crie mocks simples de forma reutilizável. Você pode cloná-lo no GitHub através do link abaixo. O README é bastante auto-explicativo, mas espero que possa ajudar os outros no futuro.

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
        });
    });
    ....

Ele gerencia automaticamente todo o clichê mencionado acima, para que você não precise escrever todo o código de injeção simulado em todos os testes. Espero que ajude.


Recentemente, lancei o ngImprovedTesting, que deve facilitar o teste simulado no AngularJS.

Para testar o 'myService' (do módulo "myApp") com suas dependências fooService e barService, você pode fazer o seguinte no seu teste do Jasmine:

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

Para obter mais informações sobre o ngImprovedTesting, consulte sua postagem introdutória no blog: http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/


Você pode injetar zombarias em seu serviço usando $provide .

Se você tiver o seguinte serviço com uma dependência que tenha um método chamado getSomething:

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

Você pode injetar uma versão simulada de myDependency da seguinte maneira:

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');
  }));

});

Note que, devido à chamada para $provide.value você não precisa injetar explicitamente o myDependency em nenhum lugar. Acontece sob o capô durante a injeção do myService. Ao configurar mockDependency aqui, poderia facilmente ser um espião.

Obrigado ao loyalBrown pelo link para esse ótimo vídeo .







angularjs-service