developer1
developer1

Reputation: 557

Angular 8 testing component with karma fails

I'm trying to start testing my component. The first thing that I wanted to test is if the ngOnInit calls the correct services.

agreement.component.ts:

  constructor(private agreementService: AgreementService,
              private operatorService: OperatorService,
              private accountService: AccountService,
              private route: ActivatedRoute,
              private router: Router,
              private sessionService: SessionService,
              private settingsService: SettingsService) {
    this.agreementId = Number(this.route.snapshot.paramMap.get('agreementId'));
  }

  async ngOnInit() {
    this.session = await this.sessionService.getSession();
    this.settings = await this.settingsService.getSettings();

    this.operatorService.getOperators(this.session.bic).subscribe(data => {
      this.operators = data;
    });
  ...
  }

agreement.component.spec.ts

import {AgreementComponent} from './agreement.component';
import {async, TestBed} from '@angular/core/testing';
import {ActivatedRoute, convertToParamMap, Router} from '@angular/router';
import {RouterTestingModule} from '@angular/router/testing';
import {AgreementService} from '../../../services/agreement.service';
import {AccountService} from '../../../services/account.service';
import {SessionService} from '../../../services/session.service';
import {SettingsService} from '../../../services/settings.service';

describe('agreementComponent', () => {
  let mockAgreementService: AgreementService;
  let mockOperatorService;
  let mockAccountService: AccountService;
  let mockRoute: ActivatedRoute;
  let mockRouter: Router;
  let mockSessionService: SessionService;
  let mockSettingsService: SettingsService;
  let component: AgreementComponent;

  beforeEach(async(() => {
    mockAgreementService = jasmine.createSpyObj(['getAgreement']);
    mockOperatorService = jasmine.createSpyObj(['getOperators']);
    mockAccountService = jasmine.createSpyObj(['getFeeAccounts']);
    mockRoute = jasmine.createSpyObj(['route']);
    mockRouter = jasmine.createSpyObj(['router']);
    mockSessionService = jasmine.createSpyObj(['getSession']);
    mockSettingsService = jasmine.createSpyObj(['getSettings']);

    TestBed.configureTestingModule({
      declarations: [AgreementComponent],
      imports: [
        RouterTestingModule
      ],
      providers: [
        {
          provide: ActivatedRoute, useValue:
            {
              snapshot: {
                paramMap: convertToParamMap({agreementId: '0'})
              }
            }
        },
      ]
    });

    component = new AgreementComponent(mockAgreementService, mockOperatorService, mockAccountService,
      mockRoute, mockRouter, mockSessionService, mockSettingsService);
  }));


  it('should call operators service', () => {
    component.ngOnInit();

    expect(mockOperatorService).toHaveBeenCalled();

  });
});

Currently I'm getting:

Failed: Cannot read property 'paramMap' of undefined

TypeError: Cannot read property 'ngOnInit' of undefined

I'm really sure this code lacks a lot of things in order to work fine, I just can't figure out what exactly is missing and what should be done differently, because googling my errors got me confused with ton of different solutions. I'm pretty new with angular testing so would like to have some pieces of advice how to write tests in the correct way.

Upvotes: 1

Views: 429

Answers (1)

Shashank Vivek
Shashank Vivek

Reputation: 17514

Take a different approach by creating Stubs as explained in one of my articles.

  1. Created reusable stubs as:
export class MockOperatorService{
  getOperators(){
     return of({data: "someVal"})
  }
}

and so on for other other services.

  1. Use RouterTestingModule as and when required in imports

  2. Mock ActivatedRoute and other services in as below:


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AgreementComponent],
      imports: [
        RouterTestingModule
      ],
      providers: [
        {
          provide: ActivatedRoute, useValue:
            {
              snapshot: {
                paramMap: convertToParamMap({agreementId: '0'})
              }
            }
        },
        {provide: OperatorService , useClass: MockOperatorService},
        {....similarly for AgreementService etc etc}
      ]
    });
  }));

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

I realized that there is a lack of proper set of articles to learn about angular testing so I wrote collection of articles which you can find on the bottom of this page. I hope it'll help

Update:

To spy as asked in comment, you can do:


  it('should call getOperators service in ngOnInit', () => {
    spyOn(component.operatorService,"getOperators").and.callThrough();
    component.ngOnInit();
    expect(component.operatorService.getOperators).toHaveBeenCalled();
    // you can also be more specific by using ".toHaveBeenCalledWith()"
  });

Upvotes: 1

Related Questions