如何模拟服务?
How to mock service?
我的 LoginComponent 中有登录功能:
login() {
this.loading = true;
this.subscription = this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
this.em.changeNav(1);
this.loading = false;
this.Auth.setToken(result);
this.router.navigate(['/code']);
this.subscription.unsubscribe();
},
err => {
this.error = JSON.parse(err._body).error;
this.loading = false;
});
}
this.authenticationService.login
是向 api...
发送 http 请求的服务
这是测试:
it('should login', fakeAsync(() => {
spyOn(component, 'login');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
//CHECK IF LOGIN FUNCTION CALLED
fixture.whenStable().then(() => {
expect(component.login).toHaveBeenCalled();
})
}));
如何在订阅方法中模拟 this.authenticationService.login
服务和断言?
编辑
测试:
import { async, ComponentFixture, TestBed, fakeAsync, tick, inject } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import {Router} from '@angular/router';
import { Http, Request, RequestOptionsArgs, Response, XHRBackend, RequestOptions, ConnectionBackend, Headers, HttpModule, BaseRequestOptions } from '@angular/http';
import {LoginService} from './login.service';
import {
MockBackend,
MockConnection
} from '@angular/http/testing';
import {EmitterService} from '../emitter.service';
import {AuthTokenService} from '../auth-token.service';
import { LoginComponent } from './login.component';
import {Observable} from 'rxjs';
describe('LoginComponent', () => {
let backend: MockBackend;
let service: LoginService;
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
class LoginServiceStub {
login() { }
};
class RouterStub {
navigate(url: string) { return url; }
}
TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [
FormsModule,
HttpModule,
ReactiveFormsModule,
RouterTestingModule
],
providers: [
LoginService,
EmitterService,
AuthTokenService,
{ provide: LoginService, useClass: LoginServiceStub },
// { provide: Router, useClass: RouterStub }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Should log in and navigate to dashboard', fakeAsync(inject([LoginService, Router], (authService: LoginService, router: Router) => {
spyOn(component, 'login');
let button = fixture.debugElement.nativeElement.querySelector('button');
spyOn(authService, 'login').and.returnValue(Observable.of(true));
button.click();
tick();
expect(component.login).toHaveBeenCalled();
expect(component.loading).toBe(false);
})));
});
问题是 login
组件中的函数从未被调用 当我 console.log 在组件的 login
方法中时,它显示消息...
这是Html部分:
<form name="form" class="form-horizontal" (ngSubmit)="f.form.valid && login()" #f="ngForm" novalidate>
<img class="loading-img" *ngIf="loading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !username.valid }">
<label for="username" class="cols-sm-2 control-label">Email</label>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user fa" aria-hidden="true"></i></span>
<input type="text" class="form-control" name="username" placeholder="Your email" [(ngModel)]="model.username" #username="ngModel" required />
</div>
</div>
<div *ngIf="f.submitted && !username.valid" class="help-block">Email is required</div>
</div>
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !password.valid }">
<label for="password" class="cols-sm-2 control-label">Password</label>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-lock fa-lg" aria-hidden="true"></i></span>
<input type="password" placeholder="Your password" class="form-control" name="password" [(ngModel)]="model.password" #password="ngModel" required />
</div>
</div>
<div *ngIf="f.submitted && !password.valid" class="help-block">Password is required</div>
</div>
<div class="form-group">
<button id="login" type="submit" class="btn btn-primary">Login</button>
<div *ngIf="error" style="margin-top: 20px;" class="text-center alert alert-danger">{{error}}</div>
</div>
<div class="form-group text-center login-down" >
<a routerLink="/register" routerLinkActive="active">Register now</a>
<a routerLink="/forgot" routerLinkActive="active">Forgot password</a>
</div>
</form>
您可以为服务创建模拟 class:
class AuthenticationServiceStub {
login() {}
};
然后在configureTestingModule
中提供:
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: AuthenticationService, useClass: AuthenticationServiceStub },
{ provide: Router, useClass: RouterStub }
]
})
在你的测试中注入
inject([AuthenticationService, Router],
(authService: AuthenticationService, router: Router) =>
将其包装在 async(+whenStable
) 或 fakeAsync(+tick
) 或使用 jasmine.done直接等待异步方法的执行
it('Should log...', fakeAsync(inject([AuthenticationService, Router]
和模拟 login
方法,如:
spyOn(authService, 'login').and.returnValue(Observable.of(true) );
这是完整的规范:
describe('Welcome component tests', () => {
let comp: TestComponent;
let fixture: ComponentFixture<TestComponent>;
let de: DebugElement;
let el: HTMLElement;
beforeEach(async(() => {
class AuthenticationServiceStub {
login() {}
};
class RouterStub {
navigateByUrl(url: string) { return url; }
}
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: AuthenticationService, useClass: AuthenticationServiceStub },
{ provide: Router, useClass: RouterStub }
]
})
.compileComponents()
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css('.login'));
el = de.nativeElement;
fixture.detectChanges();
});
it('Should log in and navigate to dashboard', fakeAsync(inject([AuthenticationService, Router], (authService: AuthenticationService, router: Router) => {
const spy = spyOn(router, 'navigateByUrl');
spyOn(authService, 'login').and.returnValue(Observable.of(true) );
el.click();
tick();
const navArgs = spy.calls.first().args[0];
expect(navArgs).toBe('/dashboard');
})));
});
我的 LoginComponent 中有登录功能:
login() {
this.loading = true;
this.subscription = this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
this.em.changeNav(1);
this.loading = false;
this.Auth.setToken(result);
this.router.navigate(['/code']);
this.subscription.unsubscribe();
},
err => {
this.error = JSON.parse(err._body).error;
this.loading = false;
});
}
this.authenticationService.login
是向 api...
这是测试:
it('should login', fakeAsync(() => {
spyOn(component, 'login');
let button = fixture.debugElement.nativeElement.querySelector('button');
button.click();
//CHECK IF LOGIN FUNCTION CALLED
fixture.whenStable().then(() => {
expect(component.login).toHaveBeenCalled();
})
}));
如何在订阅方法中模拟 this.authenticationService.login
服务和断言?
编辑
测试:
import { async, ComponentFixture, TestBed, fakeAsync, tick, inject } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import {Router} from '@angular/router';
import { Http, Request, RequestOptionsArgs, Response, XHRBackend, RequestOptions, ConnectionBackend, Headers, HttpModule, BaseRequestOptions } from '@angular/http';
import {LoginService} from './login.service';
import {
MockBackend,
MockConnection
} from '@angular/http/testing';
import {EmitterService} from '../emitter.service';
import {AuthTokenService} from '../auth-token.service';
import { LoginComponent } from './login.component';
import {Observable} from 'rxjs';
describe('LoginComponent', () => {
let backend: MockBackend;
let service: LoginService;
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
class LoginServiceStub {
login() { }
};
class RouterStub {
navigate(url: string) { return url; }
}
TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [
FormsModule,
HttpModule,
ReactiveFormsModule,
RouterTestingModule
],
providers: [
LoginService,
EmitterService,
AuthTokenService,
{ provide: LoginService, useClass: LoginServiceStub },
// { provide: Router, useClass: RouterStub }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('Should log in and navigate to dashboard', fakeAsync(inject([LoginService, Router], (authService: LoginService, router: Router) => {
spyOn(component, 'login');
let button = fixture.debugElement.nativeElement.querySelector('button');
spyOn(authService, 'login').and.returnValue(Observable.of(true));
button.click();
tick();
expect(component.login).toHaveBeenCalled();
expect(component.loading).toBe(false);
})));
});
问题是 login
组件中的函数从未被调用 当我 console.log 在组件的 login
方法中时,它显示消息...
这是Html部分:
<form name="form" class="form-horizontal" (ngSubmit)="f.form.valid && login()" #f="ngForm" novalidate>
<img class="loading-img" *ngIf="loading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !username.valid }">
<label for="username" class="cols-sm-2 control-label">Email</label>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-user fa" aria-hidden="true"></i></span>
<input type="text" class="form-control" name="username" placeholder="Your email" [(ngModel)]="model.username" #username="ngModel" required />
</div>
</div>
<div *ngIf="f.submitted && !username.valid" class="help-block">Email is required</div>
</div>
<div class="form-group" [ngClass]="{ 'has-error': f.submitted && !password.valid }">
<label for="password" class="cols-sm-2 control-label">Password</label>
<div class="cols-sm-10">
<div class="input-group">
<span class="input-group-addon"><i class="fa fa-lock fa-lg" aria-hidden="true"></i></span>
<input type="password" placeholder="Your password" class="form-control" name="password" [(ngModel)]="model.password" #password="ngModel" required />
</div>
</div>
<div *ngIf="f.submitted && !password.valid" class="help-block">Password is required</div>
</div>
<div class="form-group">
<button id="login" type="submit" class="btn btn-primary">Login</button>
<div *ngIf="error" style="margin-top: 20px;" class="text-center alert alert-danger">{{error}}</div>
</div>
<div class="form-group text-center login-down" >
<a routerLink="/register" routerLinkActive="active">Register now</a>
<a routerLink="/forgot" routerLinkActive="active">Forgot password</a>
</div>
</form>
您可以为服务创建模拟 class:
class AuthenticationServiceStub {
login() {}
};
然后在configureTestingModule
中提供:
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: AuthenticationService, useClass: AuthenticationServiceStub },
{ provide: Router, useClass: RouterStub }
]
})
在你的测试中注入
inject([AuthenticationService, Router],
(authService: AuthenticationService, router: Router) =>
将其包装在 async(+whenStable
) 或 fakeAsync(+tick
) 或使用 jasmine.done直接等待异步方法的执行
it('Should log...', fakeAsync(inject([AuthenticationService, Router]
和模拟 login
方法,如:
spyOn(authService, 'login').and.returnValue(Observable.of(true) );
这是完整的规范:
describe('Welcome component tests', () => {
let comp: TestComponent;
let fixture: ComponentFixture<TestComponent>;
let de: DebugElement;
let el: HTMLElement;
beforeEach(async(() => {
class AuthenticationServiceStub {
login() {}
};
class RouterStub {
navigateByUrl(url: string) { return url; }
}
TestBed.configureTestingModule({
declarations: [TestComponent],
providers: [
{ provide: AuthenticationService, useClass: AuthenticationServiceStub },
{ provide: Router, useClass: RouterStub }
]
})
.compileComponents()
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
comp = fixture.componentInstance;
de = fixture.debugElement.query(By.css('.login'));
el = de.nativeElement;
fixture.detectChanges();
});
it('Should log in and navigate to dashboard', fakeAsync(inject([AuthenticationService, Router], (authService: AuthenticationService, router: Router) => {
const spy = spyOn(router, 'navigateByUrl');
spyOn(authService, 'login').and.returnValue(Observable.of(true) );
el.click();
tick();
const navArgs = spy.calls.first().args[0];
expect(navArgs).toBe('/dashboard');
})));
});