Search code examples
angularunit-testingtestingjasmine

Error this.http.post is not a function with Jasmine


I just started testing my software with Jasmine and Karma (haven't done any unit testing not E2E yet so I'm just a newbie here).

What I have been asked to do is to create a generic API service that you call everytime you need to make an HTTP request, in order to avoid repeating code and making programming faster. I show you what I already have:

api.service.ts

import { HttpClient, HttpContext, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { Observable } from 'rxjs'

@Injectable({
  providedIn: 'root'
})
export class ApiService {
  headers: ApiRequests = {
    httpHeaderAccept: 'application/json',
    context: new HttpContext(),
    params: new HttpParams()
  }

  constructor(private _http: HttpClient) {}

  post<T>(url: string, body: object): Observable<T> {
    return this._http.post<T>(url, body, { ...this.headers })
  }
}

I do not show the rest of the HTTP methods because they are very similar, POST will do the trick here.

And now this is my .spec.ts file:

import { HttpClient } from '@angular/common/http'
import { TestBed } from '@angular/core/testing'
import { of } from 'rxjs'
import { ApiService } from './api.service'

describe('ApiService', () => {
  let service: ApiService
  let httpClientSpyPost: {
    post: jasmine.Spy
  }

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        ApiService,
        { provide: HttpClient, useValue: httpClientSpyPost }
      ]
    })
    service = TestBed.inject(ApiService)
    httpClientSpyPost = jasmine.createSpyObj('HttpClient', ['post'])
  })

  it('should be created', () => {
    expect(service).toBeTruthy()
  })

  it('(POST) -> should return a response with a body', (done: DoneFn) => {
    const body = {}
    const expectedResponse = {}
    httpClientSpyPost.post.and.returnValue(of(expectedResponse))
    service.post('url', body).subscribe((response) => {
      expect(response).toEqual(expectedResponse)
      done()
    })
  })
})

And this is the error Jasmine is throwing me:

TypeError: this._http.post is not a function

I don't really know what it does really mean and I think I've set up everything correct and tried some things I've read here.


Solution

  • import { HttpClient } from '@angular/common/http'
    import { TestBed } from '@angular/core/testing'
    import { of } from 'rxjs'
    import { ApiService } from './api.service'
    
    describe('ApiService', () => {
      let service: ApiService
      let httpClientSpyPost: {
        post: jasmine.Spy
      }
    
      beforeEach(() => {
        httpClientSpyPost = jasmine.createSpyObj('HttpClient', ['post'])
    
        TestBed.configureTestingModule({
          providers: [
            ApiService,
            { provide: HttpClient, useValue: httpClientSpyPost }
          ]
        })
        service = TestBed.inject(ApiService)
      })
    
      it('should be created', () => {
        expect(service).toBeTruthy()
      })
    
      it('(POST) -> should return a response with a body', (done: DoneFn) => {
        const body = {}
        const expectedResponse = {}
        httpClientSpyPost.post.and.returnValue(of(expectedResponse))
        service.post('url', body).subscribe((response) => {
          expect(response).toEqual(expectedResponse)
          done()
        })
      })
    })
    

    Try assign httpClientSpyPost before TestBed being configured. It looks like you passing undefined value and later on your assign don't have effect.