I am trying to write a test for a Component, but I always get the error: "Error: Error in ./ExpenseOverviewComponent class ExpenseOverviewComponent - inline template:41:8 caused by: No provider for Location Strategy!"
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import { FormsModule } from "@angular/forms";
import { RouterModule, Router, ActivatedRoute } from '@angular/router';
import { RouterStub, ActivatedRouteStub } from '../../../../utils/testutils';
import { HttpModule } from "@angular/http";
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { ExpenseOverviewComponent } from './expense-overview.component';
import { ExpenseFilterPipe } from '../pipes/expense-filter.pipe';
import { ExpenseService } from '../services/expense.service';
describe('ExpenseOverviewComponent', () => {
let expenseOverviewComponent: ExpenseOverviewComponent;
let fixture: ComponentFixture<ExpenseOverviewComponent>;
let debugElement: DebugElement;
let htmlElement: HTMLElement;
let spy: jasmine.Spy;
let expenseService: ExpenseService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FormsModule, RouterModule, HttpModule],
declarations: [ExpenseOverviewComponent, ExpenseFilterPipe],
providers: [
ExpenseService, { provide: Router, useClass: RouterStub },
{ provide: ActivatedRoute, useClass: ActivatedRouteStub }
]
});
fixture = TestBed.createComponent(ExpenseOverviewComponent);
expenseOverviewComponent = fixture.componentInstance;
// Expense service actually injected into the component
expenseService = fixture.debugElement.injector.get(ExpenseService);
// query for the title <panel-heading> by CSS class selector
debugElement = fixture.debugElement.query(By.css('.table'));
htmlElement = debugElement.nativeElement;
});
it('should not show expenses before OnInit', () => {
spy = spyOn(expenseService, 'getExpenses').and.returnValue(new BehaviorSubject([]).asObservable());
expect(spy.calls.any()).toBe(false, 'getExpenses not yet called');
});
});
The component under test is the following:
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Expense } from '../model/expense';
import { ExpenseService } from '../services/expense.service';
@Component({
template: require('./expense-overview.component.html'),
styles: [require('./expense-overview.component.css')]
})
export class ExpenseOverviewComponent implements OnInit {
expenseFilter: string = '';
errorMessage: string;
expenses: Expense[];
constructor(private expenseService: ExpenseService) { }
ngOnInit(): void {
this.expenseService.getExpenses()
.subscribe(expenses => this.expenses = expenses, error => this.errorMessage = <any>error);
}
deleteExpense(expense: Expense) {
this.expenseService.deleteExpense(expense)
.subscribe(response => {
this.expenses = this.expenses.filter(rec => rec.id !== expense.id);
},
error => {
console.error("Error deleting expense with id: " + expense.id);
return Observable.throw(error);
});
}
}
Does anyone see the problem? I am bundling with webpack.
var path = require('path');
// Webpack Plugins
var ProvidePlugin = require('webpack/lib/ProvidePlugin');
var DefinePlugin = require('webpack/lib/DefinePlugin');
var ENV = process.env.ENV = process.env.NODE_ENV = 'test';
/*
* Config
*/
module.exports = {
resolve: {
cache: false,
extensions: ['','.ts','.js','.json','.css','.html']
},
devtool: 'inline-source-map',
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
// remove TypeScript helpers to be injected below by DefinePlugin
'compilerOptions': {
'removeComments': true,
'noEmitHelpers': true,
},
'ignoreDiagnostics': [
2403, // 2403 -> Subsequent variable declarations
2300, // 2300 Duplicate identifier
2374, // 2374 -> Duplicate number index signature
2375 // 2375 -> Duplicate string index signature
]
},
exclude: [ /\.e2e\.ts$/, /node_modules/ ]
},
{ test: /\.json$/, loader: 'json-loader' },
{ test: /\.html$/, loader: 'raw-loader' },
{ test: /\.css$/, loader: 'raw-loader' },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url', query: { limit: 25000 } }
],
postLoaders: [
// instrument only testing sources with Istanbul
{
test: /\.(js|ts)$/,
include: root('src'),
loader: 'istanbul-instrumenter-loader',
exclude: [
/\.e2e\.ts$/,
/node_modules/
]
}
],
noParse: [
/zone\.js\/dist\/.+/,
/angular2\/bundles\/.+/
]
},
stats: { colors: true, reasons: true },
debug: false,
plugins: [
new DefinePlugin({
// Environment helpers
'process.env': {
'ENV': JSON.stringify(ENV),
'NODE_ENV': JSON.stringify(ENV)
},
'global': 'window',
// TypeScript helpers
'__metadata': 'Reflect.metadata',
'__decorate': 'Reflect.decorate'
}),
new ProvidePlugin({
// '__metadata': 'ts-helper/metadata',
// '__decorate': 'ts-helper/decorate',
'__awaiter': 'ts-helper/awaiter',
'__extends': 'ts-helper/extends',
'__param': 'ts-helper/param',
'Reflect': 'es7-reflect-metadata/dist/browser'
})
],
// we need this due to problems with es6-shim
node: {
global: 'window',
progress: false,
crypto: 'empty',
module: false,
clearImmediate: false,
setImmediate: false
}
};
Instead of importing the RouterModule
, you should import the RouterTestingModule
from @angular/router/testing
. This module factors out some things from the RouterModule
that won't work in a test environment.
See also: