Unit testing the service itself in Angular with SnackBar call

1.6k Views Asked by At

I am relatively new to Angular Material unit testing and also to Service unit testing. I created a service for my application with which I can call a Material SnackBar open function and passing a message parameter through. It's working really fine in every component when it needed but the unit testing for it is something... terrible to me.

import { Injectable } from '@angular/core';
import { MatSnackBar, MatSnackBarRef, MatSnackBarConfig } from '@angular/material/snack-bar';

@Injectable({
  providedIn: 'root',
})
export class NotificationService {
  private snackBarConfig: MatSnackBarConfig;
  private snackBarRef: MatSnackBarRef<any>;
  private snackBarAutoHide = '5000';

  constructor(private sb: MatSnackBar) {}

  openSnackBar(message) {
    this.snackBarConfig = new MatSnackBarConfig();
    this.snackBarConfig.duration = parseInt(this.snackBarAutoHide, 0);
    this.sb.open(message, 'Dismiss', this.snackBarConfig);
  }
}

And the so-called test specification file is this:

import { Overlay } from '@angular/cdk/overlay';
import { TestBed } from '@angular/core/testing';
import { MatSnackBar } from '@angular/material/snack-bar';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { NotificationService } from './notification-service';

describe('NotificationService testing:', () => {
  let spectator: SpectatorService<NotificationService>;
  const createService = createServiceFactory({
    service: NotificationService,
    providers: [MatSnackBar, Overlay],
    entryComponents: [],
    mocks: [NotificationService],
  });

  beforeEach(() => (spectator = createService()));

  it('should openSnackBar() be known as function', () => {
    expect(typeof spectator.service.openSnackBar).toEqual('function');
  });

  it('should be created', () => {
    const dateService = spectator.inject(NotificationService);
    spyOn(spectator.service, 'openSnackBar');
    dateService.openSnackBar('Test');
    expect(spectator.service.openSnackBar).toHaveBeenCalledWith('Test');
  });
});

My question would be the followings:

  1. Why my karma coverage says the openSnackBar(message) method is not tested? I mean at least I testing if it is a function or not.
  2. How can I actually test a service like this on its own?

Thank you in advance.

1

There are 1 best solutions below

0
On

Okay I solved it with little help from a friend. first problem is in my constructor I define a private, and not a public version of SnackBar (private sb: MatSnackBar -> public sb: MatSnackBar). Secondly I modified the test file a little bit and now it looks like this and working fine:

import { Overlay } from '@angular/cdk/overlay';
import { TestBed } from '@angular/core/testing';
import { MatSnackBar, MatSnackBarModule, MatSnackBarConfig } from '@angular/material/snack-bar';
import { createServiceFactory, SpectatorService } from '@ngneat/spectator';
import { NotificationService } from './notification-service';

describe('NotificationService testing:', () => {
  let spectator: SpectatorService<NotificationService>;
  let snackBar: MatSnackBar;
  let snackBarConfig: MatSnackBarConfig;

  const createService = createServiceFactory({
    service: NotificationService,
    imports: [MatSnackBarModule],
    providers: [MatSnackBarModule, Overlay],
    entryComponents: [],
    mocks: [NotificationService],
  });

  beforeEach(() => {
    TestBed.configureTestingModule({});
    snackBar = TestBed.inject(MatSnackBar);
    spectator = createService();
  });

  it('should service be created', () => {
    const service: NotificationService = TestBed.inject(NotificationService);
    expect(service).toBeTruthy();
  });

  it('should openSnackBar() be known as function', () => {
    expect(typeof spectator.service.openSnackBar).toEqual('function');
  });

  it(`should openSnackBar() call SnackBar's own open()`, () => {
    const service: NotificationService = TestBed.inject(NotificationService);
    snackBarConfig = new MatSnackBarConfig();
    snackBarConfig.duration = parseInt('5000', 0);
    const serviceSpy = spyOn(service, 'openSnackBar').and.callThrough();
    const snackSpy = spyOn(snackBar, 'open');
    expect(serviceSpy).not.toHaveBeenCalled();
    expect(snackSpy).not.toHaveBeenCalled();
    service.openSnackBar('Hello');
    expect(serviceSpy).toHaveBeenCalledWith('Hello');
    expect(snackSpy).toHaveBeenCalledWith('Hello', 'Dismiss', snackBarConfig);
  });
});

Hope it helps if anybody is struggling with the same problem in the future. Also I am sure, there is a better, nicer way to do this but right now it's working and giving 100% coverage, which counts. But will warmly welcome modification suggestions in the future to this.