Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

error with angular2 testing component with Testbed

trying to learn this testing utility TestBed in angular-2 with a simple example and have hit my first blocker. google or SO search didn't yield any matching example,

so, I have a very basic component header as below -

import { Component } from '@angular/core';

@Component({
    selector: 'header',
    template: ''
})
export class HeaderComponent{
    public title: string;

    constructor(testparam: string){
        this.title = 'test';
    }
}

and then have its spec as below -

import { TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';

describe('HeaderComponent Test', () => {
    let component: HeaderComponent;

    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [HeaderComponent]
        });

        const fixture = TestBed.createComponent(HeaderComponent);
        component = fixture.componentInstance;
    });

    it('should have the component defined', () => {
        expect(component).toBeDefined();
    });

    it('should initialize the title to test', () => {
        expect(component.title).toBe('test');
    });
});

running the karma test is throwing - Error: No provider for String! in karma.entry.js

karma.entry.js is basically just setting the test env configuration for TestBed and then goes thru each test in my spec folder, below is my karma.entry.js

require('core-js/es6');
require('core-js/es7/reflect');

require('es6-shim');
require('reflect-metadata');
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy');
require('zone.js/dist/sync-test');
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');
require('rxjs/Rx');

const browserTesting = require('@angular/platform-browser-dynamic/testing');
const coreTesting = require('@angular/core/testing');

coreTesting.TestBed.initTestEnvironment(
   browserTesting.BrowserDynamicTestingModule,
   browserTesting.platformBrowserDynamicTesting()
);

const context = require.context('../src', true, /\.spec\.ts$/);

context.keys().forEach(context);

Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;

If I remove the parameter from the constructor of the component class, the tests pass, so I am thinking that I am missing some pre-configuration thats causing the TestBed.createComponent(HeaderComponent) not to properly compile the component's constructor with the string type parameter.

any clue what I might be missing?


UPDATE:

if it helps anyone - based on @mrkosima's answer, my updated component class now looks like below and the unit tests all pass good now :)

import { Component,  OpaqueToken, Inject } from '@angular/core';

export let TITLE_TOKEN = new OpaqueToken('title token');

@Component({
    selector: 'header',
    template: '',
    providers: [{ provide: TITLE_TOKEN, useValue: 'test' }]
})
export class HeaderComponent{
    public title: string;

    constructor(@Inject(TITLE_TOKEN) titleParam: string){
        this.title = titleParam;
    }
}
like image 856
kkap Avatar asked Feb 12 '17 19:02

kkap


People also ask

What is TestBed in Angular testing?

TestBed is the primary api for writing unit tests for Angular applications and libraries. Note: Use TestBed in tests. It will be set to either TestBedViewEngine or TestBedRender3 according to the compiler used.

Which TestBed method is used to create an Angular component under test?

The TestBed. createComponent() method is used to create an instance of the AppComponent. The spec then uses expect and matcher functions to see if the component produces the expected behavior. As a result, the spec will either pass or fail.

What is TestBed and fixture in Angular?

TestBed and Fixtures. The TestBed is the first and largest of the Angular testing utilities. It creates an Angular testing module — a @NgModule class — that you configure with the configureTestingModule method to produce the module environment for the class you want to test.


Video Answer


1 Answers

You are right that the root cause of issue in the constructor's argument.

During component instantiation Injector trying to resolve all dependencies listed in constructor. Injector looks up dependencies by type in providers. More about DI here: https://angular.io/docs/ts/latest/guide/dependency-injection.html

That means if component has constructor(authService: AuthService) { }, the Injector looking for AuthService token in providers.

The same in your case - your component depends on String. But there is no any provider with String token.

Actually, it's a mistake to list primitive type as dependency.

Instead of this OpaqueToken should be used

export let TITLE_TOKEN = new OpaqueToken('title token');

Configure token in module providers

providers: [{ provide: TITLE_TOKEN, useValue: 'title value' }]

Than inject token in component:

constructor(@Inject(TITLE_TOKEN) title: string) {
  this.title = title;
}

That's the correct usage of injecting primitive.

More details here: https://angular.io/docs/ts/latest/guide/dependency-injection.html#!#opaquetoken

PS: to test your component the TITLE_TOKEN should be added to testing module:

import {TITLE_TOKEN} from ...
TestBed.configureTestingModule({
      providers: [ { provide: TITLE_TOKEN, useValue: 'test' } ]
});

And than create test component and expect title as 'test'.

like image 52
mrkosima Avatar answered Oct 19 '22 14:10

mrkosima