javascriptangulartypescriptjasminekarma-jasmine

Cannot access 'Component' before initialization. Circular dependency in Jasmine testing. Angular 17


In the example below, after running ng test --include my-component.component.spec.ts, I'm getting this error:

An error was thrown in afterAll Uncaught ReferenceError: Cannot access 'MyComponentComponent' before initialization

I think this is due to a circular dependency, because my component access a route file where the component itself is referenced.

my-component.component.ts

import {Component, OnInit} from '@angular/core';
import {myRoutes} from './my-routes';

@Component({
  selector: 'mi-component',
  template: ``,
})
export class MyComponentComponent implements OnInit {
  ngOnInit(): void {

  }

  routesModification() {
    const routes = myRoutes;
    routes[0].path = '/new-route';
  }
}

my-component.component.spec.ts

import {ComponentFixture, TestBed} from '@angular/core/testing';
import {MyComponentComponent} from './my-component.component';

describe('MyComponentComponent class', () => {
  let component: MyComponentComponent;
  let fixture: ComponentFixture<MyComponentComponent>;
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [MyComponentComponent],
      imports: [],
      providers: [],
    }).compileComponents();

    fixture = TestBed.createComponent(MyComponentComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('El componente deberia estar creado correctamente', () => {
    expect(component).toBeTruthy();
  });
});

my-routes.ts

import {Routes} from '@angular/router';
import {MyComponentComponent} from './my-component.component';

export const myRoutes: Routes = [
  {
    path: '',
    component: MyComponentComponent,
  },
];

I need to fix this so that the test passes without touching the component or the route files, which I am not allowed. This is a problem that occurs only when I launch the test individually, when I launch the application or when I launch ng test globally, no such message appears.


Solution

  • Without Modifying component:

    We create a dummy component assign it to the default route, to bypass this error message, you can revert it to the original component on the beforeEach or afterAll, but the bottom solution is the proper one I feel.

    import { ComponentFixture, TestBed } from '@angular/core/testing';
    import { MyComponentComponent } from './my-component.component';
    import { myRoutes } from './my-routes';
    import { Component } from '@angular/core';
    
    @Component({
      selector: 'dummy',
    })
    export class DummyComponent {}
    
    describe('MyComponentComponent class', () => {
      let component: MyComponentComponent;
      let fixture: ComponentFixture<MyComponentComponent>;
      beforeEach(async () => {
        myRoutes[0].component = DummyComponent;
        await TestBed.configureTestingModule({
          declarations: [MyComponentComponent],
          imports: [],
          providers: [],
        }).compileComponents();
    
        fixture = TestBed.createComponent(MyComponentComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('El componente deberia estar creado correctamente', () => {
        expect(component).toBeTruthy();
      });
    });
    

    Modifying the component:

    I think you have accessed routes which already contains the same component (import {myRoutes} from './my-routes';) you are accessing it before it is defined (export class MyComponentComponent implements OnInit {).

    Instead try to access the routes through the router config property, then update the routes, finally use resetConfig to apply the routes again.

    import { Component, inject, OnInit } from '@angular/core';
    import { Router } from '@angular/router';
    
    @Component({
      selector: 'mi-component',
      template: ``,
    })
    export class MyComponentComponent implements OnInit {
      router = inject(Router);
      ngOnInit(): void {}
    
      routesModification() {
        const routes = this.router.config;    // <- changed here!
        routes[0].path = '/new-route';        // <- changed here!
        this.router.resetConfig(routes);      // <- changed here!
      }
    }