Angular 2/4/6/7 - 使用路由器进行单元测试
Posted
技术标签:
【中文标题】Angular 2/4/6/7 - 使用路由器进行单元测试【英文标题】:How can I unit test a component that uses the Router in Angular? 【发布时间】:2017-02-09 01:07:30 【问题描述】:在 Angular 2.0.0 中,我正在对使用路由器的组件进行单元测试。但是我得到“提供的参数与调用目标的任何签名都不匹配”。错误。在 spec.ts 中的 Visual Studio 代码中,新 Router() 以红色突出显示
如果有人能告诉我正确的语法是什么,我真的很感激?提前致谢。我的代码如下:
spec.ts
import TestBed, async from '@angular/core/testing';
import NavToolComponent from './nav-tool.component';
import ComponentComm from '../../shared/component-comm.service';
import Router from '@angular/router';
describe('Component: NavTool', () =>
it('should create an instance', () =>
let component = new NavToolComponent( new ComponentComm(), new Router());
expect(component).toBeTruthy();
);
);
组件构造函数
constructor(private componentComm: ComponentComm, private router: Router)
【问题讨论】:
【参考方案1】:您也可以只使用 RouterTestingModule 并像这样监视导航功能...
import TestBed from '@angular/core/testing';
import RouterTestingModule from '@angular/router/testing';
import Router from '@angular/router';
import MyModule from './my-module';
import MyComponent from './my-component';
describe('something', () =>
let fixture: ComponentFixture<LandingComponent>;
let router: Router;
beforeEach(() =>
TestBed.configureTestingModule(
imports: [
MyModule,
RouterTestingModule.withRoutes([]),
],
).compileComponents();
fixture = TestBed.createComponent(MyComponent);
router = TestBed.get(Router);
);
it('should navigate', () =>
const component = fixture.componentInstance;
const navigateSpy = spyOn(router, 'navigate');
component.goSomewhere();
expect(navigateSpy).toHaveBeenCalledWith(['/expectedUrl']);
);
);
【讨论】:
谢谢,这行得通!我还使用router = TestBed.get(Router)
并将我的路由器保存到夹具旁边的变量中,而不是按照angular.io/guide/testing#testbedget 中的建议将组件转换为任何变量。
谢谢,这解决了我的问题:在模拟路由器时无法读取未定义的属性'root'。
嗨@Lenny 你能解释一下什么是component.goSomewhere();在这里做什么?
@harsh 要通过此测试,组件必须有一个名为 goSomewhere()
的方法,其中包含代码 this.router.navigate([/expectedUrl'])
(将导航到 /expectedUrl
。
使用此代码我看到警告:console.warn ../../../node_modules/@angular/core/bundles/core.umd.js:27337 Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?
【参考方案2】:
这是因为 Route
有一些依赖项,它希望传递给它的构造函数。
如果您使用 Angular 组件,则不应尝试进行孤立的测试。您应该使用 Angular 测试基础设施来准备测试环境。这意味着让 Angular 创建组件,让它注入所有必需的依赖项,而不是你试图创建所有东西。
为了让你开始,你应该有类似的东西
import TestBed from '@angular/core/testing';
describe('Component: NavTool', () =>
let mockRouter =
navigate: jasmine.createSpy('navigate')
;
beforeEach(() =>
TestBed.configureTestingModule(
declarations: [ NavToolComponent ],
providers: [
provide: Router, useValue: mockRouter ,
ComponentComm
]
);
);
it('should click link', () =>
let fixture = TestBed.createComponent(NavToolComponent);
fixture.detectChanges();
let component: NavToolComponent = fixture.componentInstance;
component.clickLink('home');
expect(mockRouter.navigate).toHaveBeenCalledWith(['/home']);
);
);
或者类似的东西。您使用TestBed
从头开始配置模块以进行测试。您使用@NgModule
配置它的方式几乎相同。
这里我们只是在模拟路由器。由于我们只是单元测试,我们可能不需要真正的路由工具。我们只想确保使用正确的参数调用它。 mock 和 spy 将能够为我们捕获该呼叫。
如果您确实想使用真正的路由器,那么您需要使用RouterTestingModule
,您可以在其中配置路由。查看示例 here 和 here
另请参阅:
Angular docs on Testing 获取更多使用 Angular 测试基础架构的示例。【讨论】:
这就像一个魅力。另一个解决方案中的 TestBed.get 现在已弃用。 我使用这个代码,它可以工作。但是...我在 NgTest 控制台中也收到一个错误:“无法绑定到 'routerLink',因为它不是 'a' 的已知属性。”。我尝试导入 RouterModule 和 RoutingModule,但没有奏效。是不是因为我使用了 MockRouter,所以 Angular 在 a 元素上找不到可用的 routerlink 属性?【参考方案3】:如果我们在组件控制器中注入 Route 服务,这里有一个例子:
import TestBed, async from '@angular/core/testing';
import RouterTestingModule from '@angular/router/testing'; // Because we inject service in our component
import Router from '@angular/router'; // Just if we need to test Route Service functionality
import AppComponent from './app.component';
import DummyLoginLayoutComponent from '../../../testing/mock.components.spec'; // Because we inject service in your component
describe('AppComponent', () =>
let router: Router; // Just if we need to test Route Service functionality
beforeEach(async(() =>
TestBed.configureTestingModule(
declarations: [
AppComponent,
DummyLoginLayoutComponent // Because we inject service in our component
],
imports: [
RouterTestingModule.withRoutes([
path: 'login', component: DummyLoginLayoutComponent ,
]) // Because we inject service in our component
],
).compileComponents();
router = TestBed.get(Router); // Just if we need to test Route Service functionality
router.initialNavigation(); // Just if we need to test Route Service functionality
));
it('should create the app', async(() =>
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
));
);
我们还可以测试其他功能,例如navigate()
。以防万一:
it('should call eventPage once with /register path if event is instanceof NavigationStart', fakeAsync(() =>
spyOn(analyticService, 'eventPage');
router.navigate(['register'])
.then(() =>
const baseUrl = window.location.origin;
const url = `$baseUrl/register`;
expect(analyticService.eventPage).toHaveBeenCalledTimes(1);
expect(analyticService.eventPage).toHaveBeenCalledWith(url);
);
));
我的包含所有模拟组件的文件 (mock.components.specs.ts)
import Component from '@angular/core';
@Component(
selector: 'home',
template: '<div>Dummy home component</div>',
styleUrls: []
)
export class DummyHomeComponent
【讨论】:
【参考方案4】:Jasmine 与完整的间谍对象相比更好......
describe('Test using router', () =>
const router = jasmine.createSpyObj('Router', ['navigate']);
...
beforeEach(async(() =>
TestBed.configureTestingModule(
providers: [ provide: Router, useValue: router ],
...
);
);
【讨论】:
以上是关于Angular 2/4/6/7 - 使用路由器进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章