1

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.

1 Answer 1

1

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()"
  });

Sign up to request clarification or add additional context in comments.

6 Comments

Nice, thank you sir, it was really helpful. I believe the beforeEach setup is now working properly, now I want to test if getOperators from operatorsService is being called on ngOnInit. I'm trying to figure out how should I create spy. Should it be on component or on the service? I've tried creating it like this: spyOn(MockOperatorService.prototype, 'getOperators').and.callThrough(); component.ngOnInit(); expect(MockOperatorService.prototype.getOperators).toHaveBeenCalled(); But I'm getting error: Expected spy getOperators to have been called.
@developer1 : M glad that it helped. I have updated the answer. It also covered in the article for using spy. Take a look at those articles. Please also mark this as an answer to help others looking for similar question :)
Ok, thanks! But it still doesn't work as expected. But are you sure that I should spy component.operatorService? Shouldn't I spy my mocked service and then expect the mocked service method to be called?
Hmm, it is strange. Because my test passes when I test if sessionService.getSession() is being called. But if I try to test if settingsService.getSettings() is being called, which is like identical call in ngOnInit - it fails. What could be the difference or problem?
Also if I'm testing if async methods are being called, should my it function call be async as well?
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.