简体   繁体   English

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

[英]How to mock components with providers in Angular 4 ? - Unit test

I have a problem mocking a component with providers in Angular 4. Here is the code : 我在使用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();
    }));
});

Here is my error: 这是我的错误:

spec-bundle.js:9 Unhandled Promise rejection: No provider for AuthService! spec-bundle.js:9未处理的Promise拒绝:没有AuthService的提供者! ; ; Zone: ProxyZone ; 区域:ProxyZone; Task: Promise.then ; 任务:Promise.then; Value: Error {__zone_symbol__error: Error at Error.ZoneAwareError ( http://localhost:9876/base/config/spec-bundle.js:9:3748709 ) a……} 值:错误{__zone_symbol__error:Error.ZoneAwareError出错( http:// localhost:9876 / base / config / spec-bundle.js:9:3748709 )a ......}

Any idea about what I am doing wrong ? 关于我做错了什么的任何想法?

Thanks in advance :) 提前致谢 :)

So a couple things jump out to me. 所以有几件事情向我跳出来。 I'm not sure if they're your problem though. 我不确定他们是不是你的问题。

You're trying to stub out empty classes, use them to mock inject into your component in place of a real service, and then assign those injected services back to the stub variables. 您正在尝试删除空类,使用它们来模拟注入到组件中代替实际服务,然后将这些注入的服务分配回存根变量。 Instead, I would try to either use the legit services, or stub them out and get a separate reference to them. 相反,我会尝试使用合法服务,或者将它们存根并单独引用它们。

In the case of AuthService, if you want to provide the real service (even if later on you intercept and spy on its parts), you can just say 在AuthService的情况下,如果你想提供真实的服务(即使你以后拦截并监视它的部分),你可以说

...
providers: [AuthService]
...

If you want to mock it, you would use: 如果你想模仿它,你会使用:

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

or 要么

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

Also, and I can't double check to verify this is an issue, you were doing this all inside the beforeEach scope, not outside it (so you can later refer to those variable, assuming you wanted to). 另外,我不能仔细检查以确认这是一个问题,你在beforeEach范围内完成所有操作,而不是在它之外(所以你可以稍后参考那些变量,假设你想要)。 I would move that above your beforeEach(), like I showed above/below. 我会把它移到你的beforeEach()之上,就像我上面/下面所示。

Here's an example of what I mean. 这是我的意思的一个例子。

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. 

A few additional things (I'm relative new also, and these are things I picked up). 还有一些额外的东西(我也相对较新,这些都是我选择的东西)。 You might find it less cluttering to simply grab a reference to the injected services at the test itself. 您可能会发现,只需在测试中获取对注入服务的引用就不那么混乱了。 Example: 例:

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

I hope that makes sense lol. 我希望这是有道理的哈哈。 Point being, it's much easier to just grab it there. 重点是,在那里抓住它要容易得多。 More, you can inject as many of the services as you want to grab a handle on for that particular test. 此外,您可以根据需要注入尽可能多的服务来获取特定测试的句柄。

 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();

    }

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM