0

How to spyon an observable and mock data in this scenario. In my angular 14 app, I am writing unit tests using jasmine and karma. Following is the service(UserService) and I want to mock the observable and return mock data. It has a getUserPrefer method which call HTTP get and return ApiResp of type UserModel.

UserService.ts
export class UserService {

  constructor(private _http: HttpClient, private cService: CService) {
   }
      getUserPrefer(str: string): Observable<ApiResp<UserModel>> {
        return this._http.get<ApiResp<UserModel>>(this._cService.aConfig.customer + `v1/getuser/${str}`);
       }
}

CService.ts
export class CService {
  public get config(): IApp {
    return this._config;
  }

  public get aConfig(): IApp {
    return this._config;
  }
}

IConfig.ts
export interface IApp {
  customer: string;
}

UserService.spec.ts

import { HttpClientModule } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import {HttpClientTestingModule, HttpTestingController} 
       from '@angular/common/http/testing';

import { UserService } from './UserService';
import { Observable} from 'rxjs';


describe('UserService', () => {
  let service: UserService;
  let userModel: UserModel;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientModule]
    });
    service = TestBed.inject(UserService);
  });



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

  it('should get UserPreference', () => {
   
   service.getUserPrefer('newuserjohn');
     spyOn(service, 'getUserPrefer')
         .and.returnValue(userModel);  
  });

});

ApiResp.ts
export interface ApiResp<T = {}> {
    status: number;
    timestamp: string;
    error?: string;
    message?: string;
    payload?: T;
}

export class UserModel {
  email!: string;
  id!: string;

  constructor(res: UserModel) {
    this.email = res.email;
  }
}
user1015388
  • 1,283
  • 4
  • 25
  • 45

1 Answers1

1

You are testing UserService so I wouldn't expect that you would be mocking functions that are implemented in that service - you should be mocking dependencies only. Hence you actually want to mock the HttpClient, to do this you should use the HttpClientTestingModule.

let httpTestingController: HttpTestingController;
...
imports: [
  ...
  HttpClientTestingModule,
  // don't import the real HttpModule
  ...
]
...
httpTestingController = TestBed.inject(HttpTestingController);

...
it('should get UserPreference', waitForAsync(() => {
   service.getUserPrefer('newuserjohn').subscribe((response_ => {
     expect(reponse).toEqual(mockUser); 
   }, fail);

   const url = 'your expected URL';
   const req = httpTestingController.expectOne(url);

   // mockUser will be returned from the mock http call, hence the check in the expect above
   req.flush(mockUser); 

   expect(req.request.method).toBe('GET');
}));

https://angular.io/api/common/http/testing/HttpTestingController

wlf
  • 3,086
  • 1
  • 19
  • 29
  • I tried this, for some reason it is still calling the original method where I am refering to this._cService.aConfig.customer and throwing an error 'aConfig.customer' is undefined – user1015388 Mar 02 '23 at 23:54
  • You want it to call the original method in `UserService ` as that is what you are testing. To fix this error you need to mock `CService` (which you aren't testing) and set `customer` within that mock. – wlf Mar 03 '23 at 00:30