Angular 2单元测试Mock ActivatedRoute报错问题求助
Let's break down what's going wrong and walk through the fixes step by step.
The Root Cause of Your Error
Your main issue is in the providers configuration: you used useClass: fakeActivatedRoute, but fakeActivatedRoute is an instance of your mock, not the class itself. The useClass property expects a constructor function (a class), not an already-created object. Angular tries to instantiate this "class" (which is actually an object) and throws the "Object doesn't support this action" error as a result.
Correct Ways to Mock ActivatedRoute
Option 1: Quick Mock with useValue
This is the simplest approach for basic cases where you only need to mock the url observable:
First, import the required dependencies:
import { Observable, of } from 'rxjs'; import { UrlSegment } from '@angular/router';
Then configure your TestBed properly:
beforeEach(() => { // Create a valid UrlSegment instance (avoid plain objects!) const mockUrlSegments = [new UrlSegment('updatecoc', {})]; // Build the mock ActivatedRoute object with the url observable const mockActivatedRoute = { url: of(mockUrlSegments) }; TestBed.configureTestingModule({ imports: [ FormsModule, MessagesModule, DataTableModule, DropdownModule, ConfirmDialogModule, AccordionModule, AutoCompleteModule, DialogModule, EditorModule, HttpModule ], declarations: [MaintainCOCComponent], providers: [ MaintainCOCService, Configuration, { provide: Router, useClass: MockRouter }, { provide: ActivatedRoute, useValue: mockActivatedRoute } // Use useValue for the pre-made instance ] }); });
Option 2: Reusable Mock Class
If you need to mock additional properties (like parent) or reuse the mock across multiple tests, define a proper mock class:
import { Observable, of } from 'rxjs'; import { UrlSegment } from '@angular/router'; class MockActivatedRoute { private _url: Observable<UrlSegment[]> = of([]); parent?: MockActivatedRoute; get url(): Observable<UrlSegment[]> { return this._url; } set url(value: Observable<UrlSegment[]>) { this._url = value; } }
Then use it in your test setup:
beforeEach(() => { const mockUrlSegments = [new UrlSegment('updatecoc', {})]; const fakeActivatedRoute = new MockActivatedRoute(); fakeActivatedRoute.url = of(mockUrlSegments); // Uncomment if you need to mock the parent route // fakeActivatedRoute.parent = new MockActivatedRoute(); TestBed.configureTestingModule({ imports: [ FormsModule, MessagesModule, DataTableModule, DropdownModule, ConfirmDialogModule, AccordionModule, AutoCompleteModule, DialogModule, EditorModule, HttpModule ], declarations: [MaintainCOCComponent], providers: [ MaintainCOCService, Configuration, { provide: Router, useClass: MockRouter }, { provide: ActivatedRoute, useValue: fakeActivatedRoute } // Pass the instance with useValue ] }); });
Key Tips
- Always use
new UrlSegment()instead of plain objects: Angular's router relies on the actualUrlSegmentclass, so object literals can cause unexpected type mismatches. - Remember the difference between
useValueanduseClass:useValueprovides an existing instance, whileuseClasstells Angular to create a new instance of the class for you. - To test different route states (e.g., non-edit paths), update the
urlobservable of your mock in individual test cases.
内容的提问来源于stack exchange,提问作者Ramesh Rajendran

