0

After migrating to standalone components in Angular, how do we mock testing routes?

Let's consider a component

@Component({
 standalone: true,
 imports: [RouterModule]
 template: `<a [routerLink]="elsewhere"/>`,
})
class FooComponent {}

configurations of the test that doesn't work anymore...

  beforeEach(() => {
    TestBed.overrideComponent(FooComponent, {
      set: {
          //not possible because of type error
        imports: [RouterTestingModule.withRoutes([...])],
      },
    }).configureTestingModule({
      imports: [FooComponent],
    });
  });
  beforeEach(() => {
    TestBed.configureTestingModule({
       //no effect, in my opinion component still imports the real RouterModule
      imports: [FooComponent, RouterTestingModule.withRoutes([...])],  
    });
  });
  beforeEach(() => {
    TestBed.overrideComponent(FooComponent, {
      set: {
        imports: [RouterTestingModule],
        providers: [
          provideRoutes([...])  //doesn't have any effect
        ],
      },
    }).configureTestingModule({
      imports: [FooComponent],
    });
  });
  beforeEach(() => {
    TestBed.overrideComponent(FooComponent, {
      set: {
        imports: [],
        providers: [
          provideRouter([...])  //doesn't have any effect
        ],
      },
    }).configureTestingModule({
      imports: [FooComponent],
    });
  });

anyone have working solution for this?

George Knap
  • 843
  • 9
  • 30

1 Answers1

0

I ran into a similar issue, although mine has to do with navigating with the router. To mock out something like the router, you can just override the component level provider for Router like I am doing in my test. I am using Angular testing library which simplifies a lot of this, but you can apply it to the component itself like you are doing in your component override example:

Tests:

import { Router } from '@angular/router';
import { fireEvent, render, screen } from '@testing-library/angular';
import { FooComponent } from './foo.component';

describe('FooComponent', () => {
  it('routes on click', async () => {
    const routerSpy = jasmine.createSpyObj<Router>('Router', ['navigateByUrl']);

    await render(FooComponent, { componentProviders: [{ provide: Router, useValue: routerSpy }]});

    fireEvent.click(screen.getByRole('button', { name: 'Bar' }));
    
    expect(routerSpy.navigateByUrl).toHaveBeenCalledOnceWith('/foo');
  });
});

Actual Code:

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  standalone: true,
  template: '<button (click)="onBar()">Bar</button>'
})
export class FooComponent {
  public constructor(private _router: Router) {}

  public onBar() {
    this._router.navigateByUrl('/foo');
  }
}

I understand this might be different to what you are trying to accomplish, but depending on what you are actually trying to test, this might give you some direction. Rather than testing implementation you can just test you are interacting with the framework the way you expect. If you absolutely need to render with routes, I actually found this resource which might work for you and doesn't use angular testing library:

https://this-is-angular.github.io/angular-guides/docs/standalone-apis/configuring-the-router-using-standalone-features#configuring-the-router-for-component-tests

Hope this helps!

Josh J
  • 21
  • 1
  • 4