【问题标题】:How can I unit test a component that uses the Router in Angular?Angular 2/4/6/7 - 使用路由器进行单元测试
【发布时间】: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) {}

【问题讨论】:

    标签: angular unit-testing angular6 karma-jasmine angular7


    【解决方案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

    另请参阅:

    【讨论】:

    • 这就像一个魅力。另一个解决方案中的 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 } ],
                  ...
          });        
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-22
        • 2022-06-23
        • 2017-03-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多