Angular unit test failing setup with NullInjector error and not using Stub

109 Views Asked by At

I'm writing a simple standalone Logout component test using a service depending on HttpClient. I tried different solutions to just stub the HttpAuthService and every solution I tried so far all throw :

NullInjectorError: R3InjectorError(Standalone[LogoutComponent])[HttpAuthService -> HttpAuthService -> HttpClient -> HttpClient]: NullInjectorError: No provider for HttpClient!

The service I try to stub:

@Injectable({
  providedIn: 'root'
})
export class HttpAuthService implements AuthService {

  private userSignal = signal<User|null>(null);
  private backendURL = environment.backendURL;

  public user = computed<User | null>(this.userSignal);
  constructor(private httpClient: HttpClient) {
  }
  login(params: { email: string; password: string }): Observable<void> {
    return this.httpClient.get<any[]>(`${this.backendURL}/users?email=${params.email}`)
      .pipe(
        tap(result=> {
          if (result.length > 0) {
            this.userSignal.set({userName: result[0].email})
          }
        }),
        map( result => {
          if (result.length == 0) {
            throw new Error('User not found');
          } else {
            return undefined;
          }
        })
      )
  }
  logout(): void {
    this.userSignal.set(null);
  }
}

The Logout component:

@Component({
  selector: 'app-logout',
  standalone: true,
  imports: [MatButtonModule, MatIconModule],
  template: `
    <button mat-icon-button (click)="onLogout()" >
      <mat-icon>logout</mat-icon>
    </button>
  `,
  styles: ``
})
export class LogoutComponent {


  constructor(private authService: HttpAuthService) {
  }

  onLogout() {
    this.authService.logout();
  }
}

The Logout unittest:

describe('LogoutComponent', () => {
  let component: LogoutComponent;
  let fixture: ComponentFixture<LogoutComponent>;
  const fakeAuthService = new AuthStub();

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [LogoutComponent],
      providers: [{provide: HttpAuthService, useClass: AuthStub}]
    })
    .compileComponents();

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

export  class AuthStub implements AuthService {
  login(params: { email: string; password: string }): Observable<void> {
    return of(undefined);
  }
  logout(): void {
  }
}

Solution tried:

  • Using jasmine.createSpyObj('', {//login and logout defined}) and added to providers with {provide/useValue}
  • Using AuthStub as coded below, created a const with a new instance and and added to providers with {provide/useValue}
  • Using AuthStub as coded below, created a const with a new instance and and added to providers with {provide/useClass}

To rule out IDE (Webstorm) I also runned the test in terminal with ng test. Test setup is using default ng Karma/jasmine test setup with Playwright for e2e testing.

2

There are 2 best solutions below

1
On BEST ANSWER

Could you try this code, I think since its standalone component, it needs to be instantiated differently!

describe('LogoutComponent', () => {
  let component: LogoutComponent;
  let fixture: ComponentFixture<LogoutComponent>;

  beforeEach(async () => {
    fixture = await render(LogoutComponent, {
      providers: [
        {
          provide: HttpAuthService,
          useValue: {
            logout: () => { }
          }
        }
      ]
    });
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
0
On

Fixed it by doing nothing!

I reopened Webstorm when I saw the answer given by Naren Murali. I initially wanted to run his code but saw that his code is using a third party lib. But I also saw that his stubbing of the logout was different than mine:

His:
logout: () => { } vs mine :
logout(): void {}

When I tried his stub method test was passing. But surprisingly when I reverted the code the test was also passing. So it looks like a IDE issue, Next time I will restart my IDE. Documenting this if it happen to someone else using Webstorm