-1

I am trying to test my angular2 service by using the mockbackend, but I keep getting this error for my http.post service request.

Here is the error:enter image description here

My service code is :

      @Injectable()
    export class AuthService {
      private _jwt: string;

      constructor(private router: Router, private http: Http, private locker: Locker,
                  private authHttp: AuthHttp, private jwtHelper: JwtHelper) {
        this._jwt = localStorage.getItem('id_token');
      }

      public signIn(user: any) {
        let body = JSON.stringify(user);
        let self = this;
        return this.http.post(loginPath, body, {
          headers: contentHeaders})
          .map( function (response) { //some code here}

    }


And my spec file is as follows:

import { TestBed, async, inject } from '@angular/core/testing';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import {
  Http, BaseRequestOptions, ResponseOptions, Response, RequestMethod,
  HttpModule, XHRBackend
} from '@angular/http';
import { Locker } from 'angular2-locker';
import { AuthHttp, JwtHelper } from 'angular2-jwt';
import { MockBackend, MockConnection } from '@angular/http/testing';

class RouterStub {
  navigate() {

  }
}



describe('Auth Service Unit Tests', () => {
  let authService: AuthService = new AuthService(Router, Http, Locker, AuthHttp, JwtHelper );

  let mockbackend;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        MockBackend,
        BaseRequestOptions,

        {provide: Router, useClass: RouterStub},
        {provide: AuthService, userValue: authService},

        {
          provide: Http,
          useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
            return new Http(backend, options);
          },
          deps: [MockBackend, BaseRequestOptions]
        }

      ],
      imports: [
        HttpModule
      ]
    });

    mockbackend = TestBed.get(MockBackend);

    /* const baseResponse = new Response(new ResponseOptions({body: 'response'}));
     mockbackend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));*/
  }));

    it('should respond when we subscribe to Sign in', async(() => {
      mockbackend.connections.subscribe((connection: MockConnection) =>{
        expect(connection.request.method).toBe(RequestMethod.Post);
        connection.mockRespond(new Response(new ResponseOptions({status: 201})));

      });

        let user = {email: 'test@sample.com', password: '12345'};
        authService.signIn(user).subscribe((res: Response) => {
          expect(res).toBeDefined();
          expect(res.status).toBe(200);
        });
    }));




  });

Would highly appreciate any help! Thanks in advance

A quick update (Based on a suggestion by @Will): I made the changes to my service instantiation like shown

{provide: AuthService, useClass: AuthService}

And used it inside my test spec as follows:

authService = TestBed.get(AuthService, null)

But I am still getting the error as follows:

Failed: authService.signIn is not a function
    TypeError: authService.signIn is not a function
        at src/base.spec.ts:57308:21
VDK
  • 3
  • 1
  • 3
  • `{provide: AuthService, userValue: authService},` should be `{provide: AuthService, useValue: authService},` – Will Feb 23 '17 at 20:48
  • But also I don't think you can instantiate AuthService that way. – Will Feb 23 '17 at 20:49
  • Thanks! That was a typo. But I still get the error! – VDK Feb 23 '17 at 20:51
  • Instead you could `{provide: AuthService, useClass: AuthService}` and later when you want an instance: `authService = TestBed.get(AuthService, null);`. – Will Feb 23 '17 at 20:51
  • @Will I tried doing that, and now it gives an error for authservice.signIn() method. Here is the error: Failed: authService.signIn is not a function TypeError: authService.signIn is not a function – VDK Feb 23 '17 at 20:55

1 Answers1

0

I'd expect something like the following to work. If you continue to get errors such as TypeError: authService.signIn is not a function, you might check that AuthService is correctly exported and imported. To debug the authService instance, you could add the noted console.log. In similar tests I have written, I found no need for the async() wrapper you have.

      @Injectable()
    export class AuthService {
      private _jwt: string;

      constructor(private router: Router, private http: Http, private locker: Locker,
                  private authHttp: AuthHttp, private jwtHelper: JwtHelper) {
        this._jwt = localStorage.getItem('id_token');
      }

      public signIn(user: any) {
        let body = JSON.stringify(user);
        let self = this;
        return this.http.post(loginPath, body, {
          headers: contentHeaders})
          .map( function (response) { //some code here}

    }


And my spec file is as follows:

import { TestBed, async, inject } from '@angular/core/testing';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';
import {
  Http, BaseRequestOptions, ResponseOptions, Response, RequestMethod,
  HttpModule, XHRBackend
} from '@angular/http';
import { Locker } from 'angular2-locker';
import { AuthHttp, JwtHelper } from 'angular2-jwt';
import { MockBackend, MockConnection } from '@angular/http/testing';

class RouterStub {
  navigate() {

  }
}



describe('Auth Service Unit Tests', () => {
  let authService: AuthService;

  let mockbackend;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        MockBackend,
        BaseRequestOptions,

        {provide: Router, useClass: RouterStub},
        {provide: AuthService, useClass: AuthService},

        {
          provide: Http,
          useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
            return new Http(backend, options);
          },
          deps: [MockBackend, BaseRequestOptions]
        }

      ],
      imports: [
        HttpModule
      ]
    });

    mockbackend = TestBed.get(MockBackend);
    authService = TestBed.get(AuthService, null);

    /* const baseResponse = new Response(new ResponseOptions({body: 'response'}));
     mockbackend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));*/
  }));

    it('should respond when we subscribe to Sign in', async(() => {
      // console.log(JSON.stringify(authService));
      mockbackend.connections.subscribe((connection: MockConnection) =>{
        expect(connection.request.method).toBe(RequestMethod.Post);
        connection.mockRespond(new Response(new ResponseOptions({status: 201})));

      });

        let user = {email: 'test@sample.com', password: '12345'};
        authService.signIn(user).subscribe((res: Response) => {
          expect(res).toBeDefined();
          expect(res.status).toBe(200);
        });
    }));




  });

If this does not do the job, my general advice for this sort of problem is, start with an example test from the official Angular docs or a detailed blog like this and get it running for you locally. Then update the test slowly until it meets your needs.

Will
  • 6,601
  • 3
  • 31
  • 42
  • I tried to debug the authservice and I get it as undefined. Also, I checked the export and import for the service. All seems to be in place. :(. Also, can I ask why we use **null** while instantiating the authservice? – VDK Feb 23 '17 at 21:35
  • The null is a [notFoundValue](https://angular.io/docs/ts/latest/api/core/testing/index/TestBed-class.html#!#get-anchor); indeed looks unnecessary in this case. If `authService` is `undefined`, that's where I'd start digging. I still suspect the `async()` wrappers; might try removing those and see if `authService` gets instantiated. You may want to double check the service file name and possibly other imports, or post more of the service code and file structure. – Will Feb 23 '17 at 21:56
  • quick update--- here is how my console.log looks like. `LOG: 'right here inside thisfunction AuthService(router, http, locker, authHttp, jwtHelper) { this.router = router; this.http = http; this.locker = locker; this.authHttp = authHttp; this.jwtHelper = jwtHelper; this._jwt = localStorage.getItem('id_token'); }'` – VDK Feb 23 '17 at 22:13
  • Also, when I check this in debugger, **authservice** doesn't show me any functions on it. However, if I write **authervice.prototype** , it gives me the list of all the methods this class has. Any insigths into that? – VDK Feb 23 '17 at 22:16
  • That kinda sounds like it's `AuthService` the class rather than `authService` the instance; not certain. Make sure nothing else is setting `authService` the instance, other than the `authService = TestBed.get(AuthService);` call. – Will Feb 23 '17 at 23:01
  • Checked everything, but it still is not working. I was wondering, does it need any component to test it against with? Because as far as I know, we can test the services in isolation as well, right? Please correct me if I am wrong, it seems I am missing something here! :( :( – VDK Feb 24 '17 at 14:57
  • I am sorry I accidentally updated your answer, instead of my question! This problem is taking its toll on me I suppose! :( :( – VDK Feb 24 '17 at 20:13
  • Best advice at this point is, start with an example test from the [Angular docs](https://angular.io/docs/ts/latest/guide/testing.html#!#services-with-dependencies) or a detailed blog like [this](https://semaphoreci.com/community/tutorials/testing-angular-2-http-services-with-jasmine) and get it running for you locally. Then update the test slowly until it meets your needs. – Will Feb 27 '17 at 15:52
  • the best advice indeed. Thanks a lot! I have got it working now! :) – VDK Feb 27 '17 at 17:50
  • You're welcome. I updated my answer to include those examples and a note on the gradual debugging/improvement approach. If the answer works for you, I'd appreciate it if you'd accept it. – Will Feb 28 '17 at 15:51