Reputation: 451
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;
}
}
Upvotes: 7
Views: 4187
Reputation: 507
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'
.
Upvotes: 6