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 - 使用路由器进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

在Angular 2中使用传递变量进行路由?

如何在Angular2中使用导航进行路由时显示加载屏幕?

使用 GraphQL 的 Angular2 服务和路由解析器

Angular ui 路由器 - 重定向根本不起作用

如何对依赖于 RC3 路由器的组件进行单元测试?

Angular 和 Express 路由