Brian Kessler Brian Kessler - 2 months ago 32
TypeScript Question

How can I unit test components in AngularJS2 "final"?

I'm currently trying to write a unit test for a very simple AngularJs2 component.

This is the Typescript:

// cell.component.ts
import { Component, Input } from '@angular/core';
import Cell from './cell';

@Component({
moduleId: module.id,
selector: 'cell',
templateUrl: 'cell.component.html',
styleUrls: ['cell.component.css']
})

export class CellComponent {
@Input()
cell = Cell;
}


This is the template:

<!-- cell.component.html -->
<div class="ticTacToe--board-cell ticTacToe--board-cell--{{cell.background}}">
<div class="ticTacToe--board-cell--{{cell.displayMarker()}}">{{cell.displayMarker()}}</div>
</div>


And here is my current test:

// cell.component.spec.ts
import { async, inject, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { ReflectiveInjector } from '@angular/core';

import { CellComponent } from './cell.component';
import Cell from './cell';
import Marker from './marker.enum';

//TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());

describe('CellComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [CellComponent]
});
});

it ('should render a cell', async(() => {
TestBed.compileComponents().then(() => {
// Arrange
const fixture = TestBed.createComponent(CellComponent);
const componentUnderTest = fixture.nativeElement;
const testId = 1;
const testMarker = Marker.X;
const testCell = new Cell(1);
testCell['marker'] = testMarker;

// Act
componentUnderTest.cell = testCell;

// Assert
fixture.detectChanges();
expect(componentUnderTest.querySelectorAll('div.ticTacToe--board-cell').length).toBe(1);
expect(componentUnderTest.querySelectorAll('div.ticTacToe--board-cell--background').length).toBe(1);
expect(componentUnderTest.querySelectorAll('div.ticTacToe--board-cell--X').length).toBe(1);
expect(componentUnderTest.querySelectorAll('div.ticTacToe--board-cell--X')[0].innerText).toBe('X');
});
}));
});


This fails with:


Chrome 49.0.2623 (Windows XP 0.0.0) CellComponent should render a cell
FAILED1] [1] Failed: Uncaught (in promise): Error: Error in
app/cell.component.html:1:9 caused by: self.context.cell.displayMarker
is not a function [1] Error: Uncaught (in promise): Error: Error
in app/cell.component.html:1:9 caused by:
self.context.cell.displayMarker is not a function


But displayMarker is a function in my Cell class:

import Marker from './marker.enum';

export class Cell {
id: number;
private marker: Marker;
private background = 'background';

constructor(id: number) {
this.id = id;
}

displayMarker() {
return Marker[this.marker];
}

getMarker() {
return this.marker;
}

setMarker(marker: Marker) {
if (!this.marker) {
this.marker = marker;
}
}

declareWinner() {
this.background = 'winner';
}

isEmpty() {
return this.marker === undefined;
}

}

export default Cell;


... and when tested manually (instead of through Karma/Jasmine) this works fine.

Any ideas how I can make my unit test work?

Answer

A couple mistakes.

  1. export class CellComponent {
        @Input()
        cell = Cell;  <===========
    }
    

    You're assigning the Cell function to cell. It works at when manually testing because type information is gone, and you can assign whatever to it. So when you do pass an actual Cell instance to it, then it is fine. But it should be changed to use the type syntax

    @Input() cell: Cell;
    
  2. const fixture = TestBed.createComponent(CellComponent);
    const componentUnderTest = fixture.nativeElement;
    

    fixture.nativeElement does not give you the component under test, it give you the DOM element. Why do yo think you can do componentUnderTest.querySelectorAll?

    You should be instead be using fixture.componentInstance to get the component under test.

Comments