Brian Kessler
Brian Kessler

Reputation: 2327

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?

Upvotes: 2

Views: 105

Answers (1)

Paul Samsotha
Paul Samsotha

Reputation: 209002

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.

Upvotes: 3

Related Questions