Jasmine Angular 9 test failing because 'unreachable' stack trace at injectableDefOrInjectorDefFactory
Asked Answered
P

4

52

I created an Angular application under 4. I have migrated from version to version and am currently latest version 9. I was reviewing my tests. I have a Login component that I had 3 working tests and now all are failing. It is now returning the following:

LoginComponent should be created ...
Failed: unreachable
Error: unreachable
    at injectableDefOrInjectorDefFactory (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17302:1)
    at providerToFactory (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17402:1)
    at providerToRecord (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17349:1)
    at R3Injector.processProvider (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17165:1)
    at http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17144:1
    at http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:1400:1
    at Array.forEach (<anonymous>)
    at deepForEach (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:1400:1)
    at R3Injector.processInjectorType (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17140:1)
    at http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:16881:1

The Jasmine test is as following:

// File: login.component.spec.ts
import { async, ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule, NgForm } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { of, throwError } from 'rxjs';
import { SelectItem } from 'primeng/api';
import { Dialog } from 'primeng/dialog';
import { Header, Footer } from 'primeng/api';
import { ButtonModule } from 'primeng/button';
//
import { AlertsService } from '../../global/alerts/alerts.service';
import { UserService } from '../../net-incident/services/user.service';
import { AuthService } from '../../net-incident/services/auth.service';
import { LoginComponent } from './login.component';
import { ServerSelectionWindowComponent } from '../../net-incident/server-selection-window/server-selection-window.component';
//
describe('LoginComponent', () => {
    let sut: LoginComponent;
    let fixture: ComponentFixture<LoginComponent>;
    let alertService: AlertsService;
    const authServiceSpy = jasmine.createSpyObj('AuthService',
            ['authenticate', 'logout', 'isLoggedIn', 'isLoggedOut']);
    const userServiceSpy = jasmine.createSpyObj('UserService',
            ['emptyUser', 'getUser', 'getUserServer']);
    //
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [
                FormsModule,
                ButtonModule,
                BrowserAnimationsModule
            ],
            declarations: [
                LoginComponent,
                Dialog,
                Header,
                Footer,
                ServerSelectionWindowComponent
            ],
            providers: [
                { provide: AlertsService, useClass: AlertsService },
                { provide: AuthService, useValue: authServiceSpy },
                { provide: UserService, useClass: userServiceSpy }
            ]
        } );
        alertService = TestBed.get( AlertsService );
        TestBed.compileComponents();
    }));
    beforeEach(() => {
        fixture = TestBed.createComponent(LoginComponent);
        sut = fixture.componentInstance;
        fixture.detectChanges();
    });
    it('should be created ...', () => {
        expect( sut ).toBeTruthy();
    });
});
Pteridology answered 9/6, 2020 at 11:39 Comment(0)
P
146
providers: [
    ...
    { provide: UserService, useClass: userServiceSpy }
]

Should be changed to:

providers: [
    ...
    { provide: UserService, useValue: userServiceSpy }
]
Pteridology answered 9/6, 2020 at 11:39 Comment(2)
useValue instead of useClass did the trick, thanks!Braun
Worst. error. message. ever. Thank you :)Adroit
D
12

For me I changed useClass for useValue. Like the example below:

TestBed.configureTestingModule({
      declarations: [...],
      imports: [...],
      providers: [
        { provide: PlansService, useClass: mockPlansService },
        { provide: AuthService, useClass: mockOAuthService }
      ]
    }).compileComponents();

For this

TestBed.configureTestingModule({
      declarations: [...],
      imports: [...],
      providers: [
        { provide: PlansService, useValue: mockPlansService },
        { provide: AuthService, useValue: mockOAuthService }
      ]
    }).compileComponents();
Destrier answered 7/12, 2021 at 20:14 Comment(3)
There is an accepted answer with an impressive 68 upvotes. Why might someone prefer your approach over that approach? Are you taking advantage of new capabilities? Are there scenarios where your approach is better suited? Explanations are always useful, but are especially important here.Gernhard
@JeremyCaney I just thought that, as this is a error you get when you are starting with Angular testing, it's hard to know where to put this changes, so my answer shows a more detailed version of where exactly you need to change things to solve the problem.Destrier
That was it, had to use useValue instead of useClass.Der
K
2

You can also create a mocked class to be used as your UserService:

class MockUserService {

  myMethod(): void {
    // mocked logic
  } 
}

describe('LoginComponent', () => {
...

providers: [
    ...
    { provide: UserService, useClass: MockUserService }
]
...

});
Kellogg answered 9/6, 2020 at 13:49 Comment(1)
I used to use hand crafted mocks, but I have moved to using spy.Pteridology
L
0

Don't be me putting quotes around your values - this:

providers: [{ provide: 'SomeService', useClass: 'SomeServiceStub' }],

...should be this:

providers: [{ provide: SomeService, useClass: SomeServiceStub }],
Lucknow answered 17/4, 2023 at 19:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.