45

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();
    });
});
Phil Huhn
  • 3,307
  • 3
  • 18
  • 35

4 Answers4

119
providers: [
    ...
    { provide: UserService, useClass: userServiceSpy }
]

Should be changed to:

providers: [
    ...
    { provide: UserService, useValue: userServiceSpy }
]
Phil Huhn
  • 3,307
  • 3
  • 18
  • 35
8

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();
V. Nogueira
  • 103
  • 1
  • 4
  • 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. – Jeremy Caney Dec 08 '21 at 00:36
  • 1
    @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. – V. Nogueira May 05 '22 at 18:01
  • That was it, had to use `useValue` instead of `useClass`. – masterxilo Sep 05 '22 at 13:52
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 }
]
...

});
vhbazan
  • 1,240
  • 1
  • 17
  • 26
0

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

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

...should be this:

providers: [{ provide: SomeService, useClass: SomeServiceStub }],
Adam Cox
  • 3,341
  • 1
  • 36
  • 46