3

I have the following component which retrieves data from an Angular service:

export class MyComponent {
    constructor() {
        myService.get().then(() => {
            console.log('hello from constructor');
        });
    }
}

And then my unit test:

///////////

it('does something', () => {
    console.log('hello from unit test');
});

///////////

Unfortunately this results in the following log:

> hello from unit test
> hello from constructor

How can I make sure that the constructor finishes before running the unit test?

2
  • Your question isn't really about a constructor not being called before your tests, but about you expecting your service call to be made before the tests. Might want to isolate that (i.e. removing the constructor out of the picture) so your question is clearer. (IMO) Commented Oct 24, 2017 at 15:30
  • 1
    It isn't hello from constructor. It is hello from asynchronous code that runs in constructor. Generally a thing like that is an antipattern. Commented Oct 24, 2017 at 15:46

2 Answers 2

3

Do not use the constructor to load data, implement the OnInit interface instead.

import { OnInit } from '@angular/core';
export class MyComponent implements OnInit {

    constructor(private myService: MyService) {}

    ngOnInit() {
        myService.get().then(() => {
            console.log('hello from constructor');
        });
    }
}
  • See also the angular documentation Lifecycle Hooks.
  • Do not forget to inject your dependencies like your myService instance, I added it to the constructor.

Testing

I recommend you read over the Testing documentation. It is a lot of information but it is worth it. Here is code that you would use to unit test your component.

let comp: MyComponent ;
let fixture: ComponentFixture<MyComponent>;

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyComponent],
            providers: [
                { provide: MyService, useValue: {} }
            ]
        })
        .compileComponents(); 

    TestBed.compileComponents();
    fixture = TestBed.createComponent(MyComponent);
    comp = fixture.componentInstance;
}));


it('initializes the component', fakeAsync(() => {
    var service = TestBed.get(MyService); // get your service
    service.get = () => {
            return Promise.resolve(); // you can pass data here if the service returns something
        };

    // here you could add an expect to validate component state before the call or service completes

    comp.ngOnInit(); // call ngOnInit
    tick(); // simulate the promise being resolved

    expect(service.get.toHaveBeenCalled);
    // here you could add an expect to validate component state after the service completes
}));
Sign up to request clarification or add additional context in comments.

2 Comments

This works, thanks. But how does Angular know that the ngOnInit finished its async code before continuing and running my unit test?
With this code you are faking the service call and you are faking the asynchronousness. Look at beforeEach(async(() and it('...', fakeAsync(() and service.get = () => Promise.resolve() and the tick() calls. All of those parts play their roles to help you fake timing for the component to have the experience as if it has called a service and that answered in a delayed manner. Anyway, tick() simulates the time ticking by. Removing it would break the test.
0

Your constructor is executing before your tests, however, your constructor's code makes an async call to a service, and that is executed after your tests.

First, you should really consider moving that service call away from the constructor.

Second, when writing tests for a component you usually spy on service calls and check that they were called, you don't actually make the call, you mock it up. Look up the documentation for 'spyOn'.

Lastly, if you want something to happen before your tests, take a look at 'beforeEach'. Anyways, hope this helps.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.