Eric
Eric

Reputation: 73

Angular 4 + Jasmine Unit Tests: Reasons why fixture.detectChanges() may not work upon change of @Input() variables

Been running into this one issue and scouring the internet about fixture.detectChanges() where it does not recognize changes in @Input() when explicitly inserting mock data. There are tons of threads and docs that describe the setup but not necessarily why it would cause all of my tests to break.

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.

Removing fixture.detectChanges() seems to "resolve" this error. But now insertion of any new mock data (per spec) are not detected.

Example:

TestComponent.ts

import { TestComponent } from './test-component';
import { TestComponentModule } from './test-component.module';
import { Data } from './interfaces/data';

export class TestComponent {
    @Input() data: Data;

    displayData(){ 
        let firstName = data.first;
        let lastName = data.last;
        let fullName = firstName + ' ' + lastName;
        return fullName;
    };
}

TestComponent.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TestComponent } from './test-component';
import { TestComponentModule } from './test-component.module';

class DataMock {
    data: Data = getDataMock({
        first: 'Roger',
        last: 'Moore'
    });
};

describe('TestComponent', () => {
    'use strict';
    let testComponent: TestComponent;
    let fixture: ComponentFixture<TestComponent>;

    beforeEach(async() => {
        TestBed.configureTestingModule({
        imports: [ TestComponentModule ]
        }).compileComponents();
        fixture = TestBed.createComponent(TestComponent);
        testComponent = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('should render the app', () => {
        expect(TestComponent).toBeDefined();
    });

    describe('displayData()', () => {
        let dataMock = new DataMock;
        beforeEach(() => {
            testComponent.data = dataMock.data;
        });

        it('should return fullName', () => {
            expect(TestComponent.displayData()).toBe('Roger Moore');
        });
    });
});

So, why is instantiating class dataMock before each spec necessary for the fixture.detectChanges() to work? Is this the reason?

Upvotes: 7

Views: 10825

Answers (1)

purezen
purezen

Reputation: 643

You must create the fixture after compileComponents is executed.

beforeEach(async() => {
    TestBed.configureTestingModule({
        imports: [ TestComponentModule ]
    }).compileComponents();
});

beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    testComponent = fixture.componentInstance;
    fixture.detectChanges();
});

Upvotes: 4

Related Questions