简体   繁体   中英

Angular + Karma + Jasmine - Testing and app with a fake backend

I'm developing an angular WebApp and since the BackEnd is not still ready I'm using a fake backend ( $httpBackend ) to test my REST request.

It is defined in app.js in this way:

// we want to use $httpBackend mock
app.config(function($provide) {
  $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
});
// define our fake backend
app.run(function($http, $httpBackend, url) {
  //Escape string to be able to use it in a regular expression
    function regEsc(str) {
        return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
    }

  // Mocking the login request
  //define the success var
  var user = {username: "me@stuff.it", password: "pass"};
  var userResponse = {
       "succes": "true",
       "user":{
       "id":"8490394",
       "username": "me",
       "token": "asd8u09u900asduijjaijjHJ"
      }
  };
  //success case
  $httpBackend.whenPOST(url.login, user).respond(200,userResponse);

  //other cases
  $httpBackend.whenPOST(url.login).respond(403,{success: false, message: 'Wrong username or password'});

  //let pass all views request
  $httpBackend.whenGET(function(string){
    console.log(string);
    return true;
  }).passThrough();

  $httpBackend.whenGET( RegExp( regEsc( "views/" ) ) ).passThrough();
});

And it works properly when I run the Application with grunt serve

The problems begin when I try to run the test I worte (in Jasmine and executed trough Karma), here it is a sample test:

'use strict';

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

  // load the service's module
  beforeEach(module('cmmApp'));

  // instantiate service
  var Loginservice, $rootScope, $httpBackend, url;
  beforeEach(inject(function (_Loginservice_, _$rootScope_ , $injector, _url_) {
    Loginservice = _Loginservice_;
    $rootScope = _$rootScope_;
    url = _url_;

    //questo serve per iniettare httpBackend
    $httpBackend = $injector.get('$httpBackend');

    $httpBackend.when('POST', url.login, {username:     "me@stuff.it",password:"pass"}).respond({success: true, user:{"id":"8490394","username": "me","token": "asd8u09u900asduijjaijjHJ"}});

    $httpBackend.whenPOST(url.login).respond(403, {success: false, message: 'Wrong username or password'});
  }));

it('should return user object', function () {

    var f;

    //se i valori sono corretti ritona un oggetto che contiene l'utente
    inject(function(Loginservice, $rootScope){
      Loginservice.login({username:"me@stuff.it",password:"pass"}).then(function(res){
        f = res;
      }); //end login

      //this trigger the promise
      $rootScope.$digest();

      //this trigger the http request
      $httpBackend.flush();

      expect(f).toEqual({success: true, user:{"id":"8490394","username": "me","token": "asd8u09u900asduijjaijjHJ"}});
    }); //end inject
  });

They work perfectly if I remove the fake backend config from app.js, instead if I leave it there Karma report: Error: No pending request to flush !

I think that this is beacause of the interceptors declared in app.js are triggered and prevent the $httpBackend defined in the test to be triggered on a request.

Any idea on how to solve this issue?

Thanks in advance

ngMockE2E will make your httpBackend useless in unit tests. I struggled with the same thing. I have now been putting all my mock objects in a module, my.mocks or whatever, and then using that in e2e tests as well as in unit tests.

For e2e testing and backendless development, I have an appDev module that gets bootstrapped and requires the app module, the my.mocks module, and the ngMockE2E module.

For unit testing I load app and the mocks then do basically the same when()'s as my appDev code, it really does seem silly to me to duplicate things but at the same time I often want to test using data that will throw an error and I find myself with some custom unit test responses aside from the e2e responses.

$httpBackend won't make your life easy, and it is very much angular-centric, meaning that any XHR in your app not done through Angular won't return the fake data you defined.

Another alternative is to monkey-patch XMLHttpRequest to intercept calls to the service you want to simulate, and pass them to a pseudo-RESTful service running on the client side. That's exactly what FakeRest does, so you might want to take a look at it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM