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

1

7

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');
        });
    });
});
  • Originally, class dataMock was defined as const variable - in which case fixture.detectChanges() breaks all of the tests it's applied to.
  • Now, dataMock is a class with the mock Input() data and fixture.detectChanges() seems to work again

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

Booker answered 10/8, 2017 at 22:54 Comment(1)
I think I'm having the same problem, but using ts-mockito mocks to no avail.Zebrawood
H
4

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();
});
Hayley answered 27/6, 2018 at 15:11 Comment(1)
I am facing issues with fixture.detectChanges() can you look into this #69879626 @JacobAbandon

© 2022 - 2024 — McMap. All rights reserved.