karansys
karansys

Reputation: 2719

How to mock service function in Angular component for unit test

I am writing unit test for angular app, I am testing if the service function returns a value.

component.spec.ts

import {TopToolBarService} from '../../top-toolbar/top-toolbar.service';

beforeEach(async(() => {
   TestBed.configureTestingModule ({
   declarations: [ UsersListComponent],
   providers: [TopToolBarService],//tried mocking service here,still test failed
   schemas:[CUSTOM_ELEMENTS_SCHEMA]
 })
  .compileComponents();
}));



beforeEach(() => {
    fixture = TestBed.createComponent(UserListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });



  it('should return data from service function', async(() => {
    let mockTopToolBarService: jasmine.SpyObj<TopToolBarService>;
    mockTopToolBarService = jasmine.createSpyObj('TopToolBarService', ['getCustomer']);
    mockTopToolBarService.getCustomer.and.returnValue("king");
    fixture.detectChanges();
    expect(component.bDefine).toBe(true); //fails
  }))

component.ts

bDefine = false;
ngOnInit() {
 let customer = this.topToolBarService.getCustomer();
 if (customer == null) {
   bDefine = false;
 } else {
    bDefine = true;
   }
}

I believe I have mocked the service function in my test, so I expect it must have reached else part where variable is set to 'true'.

TopToolBarService.ts

import { EventEmitter, Injectable, Output } from "@angular/core";

@Injectable()
export class TopToolBarService {
customer = null;

  getCustomer() {
    return this.customer;
  }
}

Upvotes: 52

Views: 166871

Answers (5)

Pradeep Vasu
Pradeep Vasu

Reputation: 1

Hi please try this.

import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { AppService } from './app.service';
import { of } from 'rxjs/internal/observable/of';

describe('AppComponent', () => {

  beforeEach( () => {
    TestBed.configureTestingModule({
      imports: [AppComponent],
      providers: [AppService],
    }).compileComponents();
  });

  it('should render with mocked title', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const service = fixture.debugElement.injector.get(AppService);
    let mockData = {
      id: 1,
      title: 'Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop',
      price: 109.95,
      description:
        'Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday',
      category: "women's clothing",
      image: 'https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg',
      rating: { rate: 3.9, count: 120 },
    };

    spyOn(service, 'getData').and.returnValue(of(mockData));
  
    fixture.detectChanges();
    const compiled = fixture.nativeElement as HTMLElement;
    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptop');
    
  });


});

for more details please visit my blog

https://code-js.in/angular/understanding-unit-testing-in-angular-mocked-service-api-calls-and-component-rendering/

Upvotes: -1

Pedro Bezanilla
Pedro Bezanilla

Reputation: 1365

Try updating providers inside beforeEach(async(() => ...) and moving your mockedService variable on the top of it:

describe('Component TEST', () => {
   ...
   let mockToolBarService;
   ...
      beforeEach(async(() => {
      ...
      mockToolBarService = jasmine.createSpyObj(['getCustomer']);
      mockToolBarService.getCustomer.and.returnValue('king');
      TestBed.configureTestingModule ({
           ...
           providers: [ { provide: TopToolBarService, useValue: mockToolBarService } ]
           ...

Hope it helps!

Upvotes: 47

satanTime
satanTime

Reputation: 13539

You might consider usage of ng-mocks to avoid all that boilerplate to configure TestBed.

beforeEach(() => MockBuilder(UsersListComponent)
  .mock(TopToolBarService, {
    // adding custom behavior to the service
    getCustomer: jasmine.createSpy().and.returnValue('king'),
  })
);

it('should return data from service function', () => {
  const fixture = MockRender(UsersListComponent);
  // now right after the render the property should be true
  expect(fixtur.point.componentInstance.bDefine).toBe(true);
}));

Upvotes: 3

Islam Murtazaev
Islam Murtazaev

Reputation: 1778

Change your provider value

beforeEach(() => {
   TestBed.configureTestingModule({
      declarations: [ UsersListComponent],
      providers: [{
         provide: TopToolBarService,
         useValue: jasmine.createSpyObj('TopToolBarService', ['getCustomer'])
      }],
      schemas:[CUSTOM_ELEMENTS_SCHEMA]
     });
     mockTopToolBarService = TestBed.get(TopToolBarService);

     mockTopToolBarService.getCustomer.and.returnValue(of([])); // mock output of function
   })

Upvotes: 21

observingstream
observingstream

Reputation: 466

You have to configure the testing module before you run your code. It doesn't know about your spy object unless you pass it to the TestBed.configureTestingModule as an import.

https://angular.io/guide/testing#component-with-a-dependency

Upvotes: 1

Related Questions