Jasmine Angular 9测试失败,因为“unreachable”出现在injectableDefOrInjectorDefFactory的堆栈跟踪中。

47

我在 Angular 4 下创建了一个应用程序。我已经从一个版本迁移到另一个版本,目前是最新的版本9。我正在审查我的测试。我有一个 Login 组件,我曾经有3个可工作的测试,现在全部都失败了。它现在返回以下内容:

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

以下是Jasmine测试:

// 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();
    });
});
4个回答

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

应该改为:

providers: [
    ...
    { provide: UserService, useValue: userServiceSpy }
]

useValue 取代 useClass,解决了问题,谢谢! - MikhailRatner
6
最糟糕的错误信息。谢谢 :) - spyro

8

对我来说,我将useClass改为useValue,像下面的例子一样:

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

针对此问题

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

有一个被接受的答案,获得了令人印象深刻的68个赞。为什么有人会更喜欢你的方法而不是那种方法?你是否利用了新的功能?你的方法是否更适合某些场景?解释总是有用的,但在这里尤其重要。 - Jeremy Caney
1
@JeremyCaney 我只是认为,由于这是在开始使用Angular测试时遇到的错误,很难知道在哪里进行更改,因此我的答案显示了更详细的版本,告诉你需要在哪些地方进行更改以解决问题。 - V. Nogueira
就是这样,必须使用useValue而不是useClass - masterxilo

2
您可以创建一个模拟类作为您的UserService:
class MockUserService {

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

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

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

});

我曾经使用手工模拟,但现在已转为使用 Spy。 - Phil Huhn

0
不要像我一样在你的值周围加上引号 - 这样:
providers: [{ provide: 'SomeService', useClass: 'SomeServiceStub' }],

应该是这样的:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接