首页 > 解决方案 > 在不模拟路由器的情况下测试 ActivatedRoute.paramMap

问题描述

我想测试一个组件this.activatedRoute.paramMap在我的测试中是如何处理的,而不模拟ActivatedRoute(即使用RouterTestingModule,没有间谍或模拟)。

在下面的stackblitz中,我设置了一个相当简单的组件来监听id路由参数:

@Component({ /* ... */})
export class RoutingExamplesComponent {
  constructor(private readonly route: ActivatedRoute, /* ... */) {}

  readonly param$ = this.route.paramMap.pipe(map(params => params.get('id') ?? '<none>'));
  // ...
}

在我的测试中,我想设置我的路由并确保参数得到很好的传播:

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [
      RoutingExamplesModule,
      RouterTestingModule.withRoutes([
        {
          path: "route/:id",
          component: RoutingExamplesComponent
        }
      ])
    ]
  });

  fixture = TestBed.createComponent(RoutingExamplesComponent);
  component = fixture.componentInstance;
  router = TestBed.get(Router);
});


it("receives initial setup", async () => {
  fixture.detectChanges();
  await router.navigate(["route", "1234"]);
  fixture.detectChanges();
  expect(fixture.nativeElement.querySelector("p").textContent).toContain(
      "1234"
    );
  });

此测试未通过,因为看起来参数未传播:

Expected '<none>' to contain '1234'.
Error: Expected '<none>' to contain '1234'. at <Jasmine> at UserContext.eval (https://angular-routing-playground-routing-test.stackblitz.io/~/app/routing-examples/routing-examples.component.spec.ts:31:80)

如何在不以任何方式模拟路由器的情况下正确获取此参数?


关于我在做什么的一些可选上下文:大多数关于路由器测试的堆栈溢出响应都建议模拟它,我认为这是一个严重的错误。一般来说,我已经成功地针对 RouterTestingModule 进行了测试,但是 paramMap 是子路由器的上下文。

标签: angulartypescriptunit-testingangular-router

解决方案


因此,经过大量调查后,我实际上找到了两种解决此问题的方法。

没有提供路由参数,因为路由器的上下文与嵌入<router-outlet>. 由于我们不在路由器出口内,我们没有绑定到出口的路由,因此我们没有传播路由参数。

解决方案 1:让它就像在路由器插座中一样

该解决方案基本上覆盖ActivatedRoute提供者以提供路由器的第一个子节点。这使用与其定义相同的注入机制,但提供了根上下文的第一个孩子而不是根上下文:

TestBed.configureTestingModule({
  imports: [
    RoutingExamplesModule,
    RouterTestingModule.withRoutes([
      // ...
    ])
  ],
  providers: [
    // Patches the activated route to make it as if we were in the
    // <router-outlet>, so we can access the route parameters.
    {
      provide: ActivatedRoute,
      useFactory: (router: Router) => router.routerState.root.firstChild,
      deps: [Router],
    }
  ],
});

权衡:这意味着导航需要在组件实例化之前触发,否则您将不会定义路由的第一个子节点:

beforeEach(async () => {
  TestBed.configureTestingModule( ... );

  router = TestBed.get(Router);
  await router.navigate(["route", "1234"]);

  fixture = TestBed.createComponent(RoutingExamplesComponent);
});

解决方案 2:在路由器插座中引导组件

这基本上意味着您实例化一个简单的组件,只需渲染<router-outlet></router-outlet>并创建这个组件。

权衡:您不再可以访问开箱即用的夹具,您需要检索调试组件。


推荐阅读