James
James

Reputation: 2860

Angular2 tests running before compileComponents variables are set

I am having an issue in an Angular2 text component where I am getting the following error when I attempt to run the testrunner:

Component: Product Component Should ensure component subscribes to service EventEmitter on instantiation
Failed: Cannot read property 'detectChanges' of undefined
TypeError: Cannot read property 'detectChanges' of undefined
Component: Product Component Should check that service getProducts is called when component getProducts is called
Failed: Cannot read property 'getProducts' of undefined
TypeError: Cannot read property 'getProducts' of undefined

Here is my test module:

import { ProductService } from "../../../../services/product.service";
import { TestBed, ComponentFixture, async } from "@angular/core/testing";
import { ProductComponent } from "../../../../components/catalog/products/ProductComponent";
import { HttpModule } from "@angular/http";
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { Observable } from "rxjs/Rx";

class MockProductService {
    emitter = Observable.of({});
    constructor() {

    }

    getProducts() {
        return this;
    }
}

let comp:    ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
let de:      DebugElement;
let el:      HTMLElement;

describe('Component: Product Component', () => {
    let mockProductService;

    beforeEach(() => {
        mockProductService = new MockProductService();

        TestBed.configureTestingModule({
            declarations: [
                ProductComponent
            ],
            providers: [
                {
                    provide: ProductService, useValue: mockProductService
                }
            ],
            imports: [
                HttpModule
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA]
        })
        .compileComponents()
            .then(() => {
                fixture = TestBed.createComponent(ProductComponent);
                comp = fixture.componentInstance;
            });
    });

    it('Should ensure component subscribes to service EventEmitter on instantiation', () => {
       // TestBed.compileComponents()
            //.then(() => {
                //spyOn(mockProductService, 'emitter').and.returnValue({
                //    subscribe: () => {}
                //});


                fixture.detectChanges();
                fixture.whenStable().then(() => {
                    expect(comp.products).toBe({});
                });
                //expect(mockProductService.emitter).toHaveBeenCalled();
            //});
    });

    it('Should check that service getProducts is called when component getProducts is called', () => {

        //TestBed.compileComponents()
         //   .then(() => {

                spyOn(mockProductService, 'getProducts').and.returnValue({
                    subscribe: () => {}
                });

                comp.getProducts({});
                expect(mockProductService.getProducts).toHaveBeenCalled();
       // });
    });


});

As the component under test uses an external template via the templateUrl property of the class I have to use the compileComponents method of the TestBed to compile this template ready for the test. As you can see this retyrns a promise which inside the call back I then define the fixture and the component instance ready for each test. As this is in the beforeEach this will be done for every test.

When I attempt to access the fixture and component instances from within my tests it is saying they are undefined which leads me to believe that the tests are being called before the promise is being returned from the compileComponents method. I have tried moving the TestBed.CompileComponents to within each test spec but this causes an error too.

Can anyone advise as to what I need to change here? Thanks

Upvotes: 1

Views: 3184

Answers (1)

jonrsharpe
jonrsharpe

Reputation: 122116

I've used both of the following methods successfully. Either use DoneFn to hold off on the beforeEach until everything's finished:

beforeEach(done => {
    mockProductService = new MockProductService();

    TestBed.configureTestingModule({
        ...
    })
    .compileComponents()
        .then(() => {
            fixture = TestBed.createComponent(ProductComponent);
            comp = fixture.componentInstance;
            done();
        });
});

Or use async and split the beforeEach into two parts:

beforeEach(async(() => {
    mockProductService = new MockProductService();

    TestBed.configureTestingModule({
        ...
    })
    .compileComponents();
}));

beforeEach(() => {
    fixture = TestBed.createComponent(ProductComponent);
    comp = fixture.componentInstance;
});

Upvotes: 2

Related Questions