1

How can I solve this?

Hello All, i am pretty new to unit test of angular 6 using karma and jasmine

I am trying to cover a service which is inside my constructor of a component but unable to do it. The 'it' block 'should create component' covers the lines inside the constructor except for the data which is reutrned by the service .

component.ts

  constructor(public firstService: FirstService, public secondService: SecondService) {

    let localData :any
    let id = this.secondService.getId()
    let initialId = this.secondService.getInitialId()

    this.firstService.getRevisions(id, initialId).subscribe((data) => {
      if (data && data.content) {
         localData = data.content || [];
      }
    });
    }

component.spec.ts

 describe('TestComponent', () => {
      let component: TestComponent;
      let fixture: ComponentFixture<TestComponent>;
      let firstService: FirstService;
      let secondService: SecondService;

     beforeEach(() => {
      TestBed.configureTestingModule({
      declarations: [TestComponent],
      providers: [FirstService, SecondService]
     })
      .compileComponents();
     });
     beforeEach(() => {
     firstService = TestBed.get(FirstService);
     fixture = TestBed.createComponent(TestComponent);
     component = fixture.componentInstance;
     secondService = TestBed.get(SecondService);
     fixture.detectChanges();
     });

    it('should create Component', () => {
    expect(component).toBeTruthy();
    const mockData = {
      "content": [
        {
          "name": "String",
        }
      ],
    }
    spyOn(firstService, "getRevisions").and.callFake(() => {
      return of({ mockData });
    });
    spyOn(secondService, 'getId');
    spyOn(secondService, 'getInitialId');

    fixture.whenStable().then(() => {
      expect(firstService.getRevisions).toHaveBeenCalled();
    });
    });
Kishore Jv
  • 159
  • 5
  • 17

1 Answers1

0

You are spying on the service after the constructor was called.

The constructor is called when the method compileComponents() is called on your TestBed.

So you have to possibilities:

First you could create a stub service which already returns a specific observable which emits a value X. You would then provide this stub in your TestBed with {provide: YourService, useValue: serviceStub} where the serviceStub could look like this: const serviceStub = {yourMethod: ()=> of(YOUR_TEST_VALUE}

The thing is though that in your test case you would not be able to expect that the service method was called but you could test, that the emitted value from your observable was set inside your component. Furthermore in case you would want to change value X for different use cases you would need to create multiple describes each providing a different version of the service mock.

A better way imho would be to move the subscription into the onInit life cycle hook.

For your app it would not make a difference and it would be easier to test.

The onInit life cycle inside a test is called when you trigger the first time fixture.detectChanges.

Having said that, if you would move the subscription into the ngOnInit method, you would only need to delete the fixture.detectChanged from the beforeEach function and move it into your test case after your spyOn.

Erbsenkoenig
  • 1,584
  • 14
  • 18