如何在Angular 4中使用提供程序模拟组件? - 单元测试

时间:2017-05-16 12:17:52

标签: angular unit-testing jasmine mocking

我在使用Angular 4中的提供程序模拟组件时遇到问题。以下是代码:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/core/platform-browser';
import { DebugElement } from '@angular/core';
import { FormsModule,
    ReactiveFormsModule,
    FormBuilder
} from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import {
    Http, ConnectionBackend,
    BaseRequestOptions
} from '@angular/http';
import { MockBackend, async } from '@angular/http/testing';

import { LoginComponent } from './../../../src/app/login/login.component';
import { LoginService } from './../../../src/app/login/login.service';
import { LoginComponent } from './../../../src/app/login/login.component';
import { LoggerService } from './../../../src/app/logger-service';
import { AuthService } from './../../../src/app/pages/auth.service';

describe('LoginComponent', () => {
    let comp: LoginComponent;
    let fixture: ComponentFixture<LoginComponent>;
    let de: DebugElement;
    let el: HTMLElement;

    beforeEach(() => {
        // implement mock
        class loggerService = {

        };

        class loginService = {

        };

        class authService = {

        };

        class router = {

        };

        TestBed.configureTestingModule({
            declarations: [ LoginComponent ],
            imports: [
                ReactiveFormsModule,
                FormsModule
            ],
            providers: [
                MockBackend,
                BaseRequestOptions,
                AuthService,
                LoginService,
                LoggerService,
                RouterModule,
                { provide: AuthService, useValue: authService },
                { provide: LoginService, useClass: LoginService },
                { provide: LoggerService, useValue: loggerService },
                {
                    provide: Http, useFactory: (backend: ConnectionBackend,
                        defaultOptions: BaseRequestOptions) => {
                        return new Http(backend, defaultOptions);
                    }, deps: [MockBackend, BaseRequestOptions]
                },
                { provide: Router, useClass: router }
            ]
        }).compileComponents().then(() => {
            fixture = TestBed.createComponent(LoginComponent);

            comp = fixture.componentInstance;

            comp.detectChanges();
            comp.ngOnInit();

            loginService = fixture.debugElement.injector.get(LoginService);
            loggerService = fixture.debugElement.injector.get(LoggerService);
            authService = fixture.debugElement.injector.get(AuthService);
            router = fixture.debugElement.injector.get(Router);
        });

    });

    it('should create component', async(() => {
        expect(comp).toBeDefined();
    }));
});

这是我的错误:

  

spec-bundle.js:9未处理的Promise拒绝:没有AuthService的提供者! ;区域:ProxyZone;任务:Promise.then;值:   错误{__zone_symbol__error:错误在Error.ZoneAwareError(http://localhost:9876/base/config/spec-bundle.js:9:3748709)a ......}

对我做错了什么的想法?

提前致谢:)

1 个答案:

答案 0 :(得分:10)

所以有几件事情向我发出。我不确定他们是否是你的问题。

您正在尝试删除空类,使用它们来模拟注入到组件中代替实际服务,然后将这些注入的服务分配回存根变量。相反,我会尝试使用合法服务,或者将它们存根并单独引用它们。

在AuthService的情况下,如果你想提供真实的服务(即使你以后拦截和监视它的部分),你可以说

...
providers: [AuthService]
...

如果你想模仿它,你会使用:

class mockAuthService{}
beforeEach(() => {
    TestBed.configureTestingModule({
    ...
    providers: [{provide: AuthService, useClass: mockAuthService}] 
    ...

let mockAuthService;
beforeEach(() => {
    mockAuthService = {}
    TestBed.configureTestingModule({
    ...
    providers: [{provide: AuthService, useValue: mockAuthService}] 
    ...

此外,我无法仔细检查以确认这是一个问题,您在beforeEach范围内进行此操作,而不是在其外部(因此您可以稍后参考这些变量,假设您想要)。我会把它移到你的beforeEach()之上,就像我上面/下面所示。

这是我的意思的一个例子。

describe('LoginComponent', () => {
    let comp: LoginComponent;
    let fixture: ComponentFixture<LoginComponent>;
    let de: DebugElement;
    let el: HTMLElement;


let authServiceReference;

beforeEach(() => {
    TestBed.configureTestingModule({
        declarations: [ LoginComponent ],
        imports: [
            ReactiveFormsModule,
            FormsModule
        ],
        providers: [
            MockBackend,
            BaseRequestOptions,
            AuthService,
            LoginService,
            LoggerService,
            RouterModule,                
            {
                provide: Http, useFactory: (backend: ConnectionBackend,
                    defaultOptions: BaseRequestOptions) => {
                    return new Http(backend, defaultOptions);
                }, deps: [MockBackend, BaseRequestOptions]
            }, 
            Router

        ]
    }).compileComponents().then(() => {
        fixture = TestBed.createComponent(LoginComponent);

        comp = fixture.componentInstance;

        comp.detectChanges();
        comp.ngOnInit();


        authServiceReference = Testbed.get(AuthService); // get service you injected above

    });

});

it('should create component', () => {
    expect(comp).toBeDefined();
}); // this part really doesn't need to be async. 

还有一些额外的东西(我也相对较新,这些都是我选择的东西)。您可能会发现,只需在测试中获取对注入服务的引用就不那么混乱了。例如:

it('should have a service', inject([SomeService], (serviceHandle: SomeService) => {
     expect(serviceHandle).toEqual(sameServiceYouTriedToGrabInInitialSetUp);
}

我希望这有道理lol。重点是,在那里抓住它会容易得多。此外,您可以注入尽可能多的服务来获取 特定测试的句柄。

 it('should have a service', inject([SomeService, SomeOtherService, YetOneMoreService], (serviceHandle: SomeService, otherServiceHandle: SomeOtherService, yetAnotherHandle: YetOneMoreService) => {
         spyOn(serviceHandle, 'isAuthenticated').and.returnsValue(true);
         spyOn(otherServiceHandle, 'getUrl').and.returnsValue(/home);
         let yahSpy = spyOn(yetAnotherHandle, 'doSomething');
         //code
          expect (yahSpy.doSomething).toHaveBeenCalled();

    }

希望这会有所帮助。