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;
}
}
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.
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.
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.
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'
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With