Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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');
        });
    });
});
  • 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?

like image 917
emok032 Avatar asked Aug 10 '17 22:08

emok032


1 Answers

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();
});
like image 190
purezen Avatar answered Sep 22 '22 01:09

purezen