Búsqueda de sitios web

javascript - ¿Cómo resuelvo una promesa en una prueba unitaria de AngularJS?


Estoy usando la configuración básica de karma/jasmine para probar mi código Angular. Aquí está mi prueba:

var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;

describe('main controller', function() {
   var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;


   beforeEach(inject(function($controller, $rootScope, $q) {
     scope = $rootScope.$new();
     $controllerConstructor = $controller;
     q = $q;
     mockSuperheroData = {
       getSuperheroes: function() {
         deferred = q.defer();
         return deferred.promise;
       }
     };
     ctr = $controllerConstructor('MainCtrl', {$scope: scope, $location: {}, superheroService: mockSuperheroData, keys: {}});
   }));

   it('should set the result of getResource to scope.heroes', function() {
     scope.getHeroes();
     expect(scope.heroes).toBe(100);
   });
}

scope.getHeroes() llama a mockSuperheroData.getSuperheroes() que está devolviendo una promesa. ¿Cómo puedo forzar que la promesa devuelva lo que quiero en la prueba unitaria? ¿Dónde puedo engancharme a la promesa para simular su retorno?

Respuesta #1

¿Cómo hago para forzar que la promesa devuelva lo que quiero en la prueba unitaria?

Básicamente necesitarás llamar a resolve en el Deferred:

deferred.resolve(100);

Puedes poner eso directamente antes del return deferred.promise o en un setTimeout asíncrono.

Respuesta #2

var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q;

describe('main controller', function() {
  var $controllerConstructor, ctr, mockSuperheroData, scope, deferred, q, rootScope;


  beforeEach(inject(function($controller, $rootScope, $q) {
    scope = $rootScope.$new();
    $controllerConstructor = $controller;
    q = $q;
    rootScope = $rootScope;
    mockSuperheroData = {
      getSuperheroes: function() {
        deferred = q.defer();
        return deferred.promise;
      }
    };
    ctr = $controllerConstructor('MainCtrl', {$scope: scope, $location: {}, superheroService: mockSuperheroData, keys: {}});
  }));

  it('should set the result of getResource to scope.heroes', function() {
    scope.getHeroes();

    deferred.resolve(100);
    rootScope.$apply();

    expect(scope.heroes).toBe(100);
  });
});

Cabe mencionar que, debido a que $q está integrado con $rootScope, no solo necesitas resolver el objeto diferido, sino que también necesitas propagar los cambios llamando a $rootScope.$apply.

Escribí un blog sobre cómo burlarse de las promesas de Angular en projectpoppycock

y escribí un ejemplo funcional, con pruebas, en plunkr

Realmente me gusta la forma en que estás inyectando el servicio simulado como una dependencia del controlador usando el servicio $controller, puede que necesite revisar mi forma de hacer esto. ¡Tu manera es mejor :) Gracias!