从组件中 returns 可观察到的服务获取模拟值的问题
Issue with getting mocked value from service which returns observable in a component
我是编写 Jasmine 测试用例的新手。我已经创建了 LoginComponent,然后它将使用 AuthService 检查服务器和 return 根据我将决定并适当显示信息的响应的 observable。
作为为此组件编写单元测试用例的一部分,我需要测试有效用户和无效用户登录。
场景一:登录无效
登录方法调用 AuthService.login() 将被订阅,如果错误消息将显示在登录页面上。
场景 2:有效登录
在这里,如果 resp 包含一些令牌,我们将认为它已通过身份验证并路由到主屏幕。
我尝试检查无效场景和面临的问题。
login.component.ts
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '../service/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private authService: AuthService, private router:Router, private activeRoute:ActivatedRoute) { }
email:string;
password:string;
message:string;
ngOnInit() {
}
submitForm()
{
this.authService.login({username: this.email, password: this.password}).subscribe
(
(resp) =>
{
if(resp['status'] == 200 )
{
sessionStorage.setItem('UserAuthenticated', 'true');
sessionStorage.setItem('Jwt_Token', resp['JWT_Token']);
this.router.navigate(['/dashboard'])
}
},
(errorResp: HttpErrorResponse) =>
{
console.log(errorResp)
this.message = errorResp['error']['errorMessage'];
}
);
}
}
auth.service.ts
import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { environment } from 'src/environments/environment';
import { URLMapping } from '../shared/URLMapping';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) { }
login(data:object)
{
return this.http.post(environment.applicationURL+URLMapping.LOGIN, data);
}
}
login.component.spec.ts
import { HttpClientModule, HttpErrorResponse } from '@angular/common/http';
import { DebugElement } from '@angular/core';
import { async, ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { AuthService } from '../service/auth.service';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let authService: AuthService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [FormsModule, HttpClientModule, RouterTestingModule],
providers: [AuthService]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
fixture.autoDetectChanges();
});
it('invalid user access', async(() => {
authService = fixture.debugElement.injector.get(AuthService);
authService = TestBed.get(AuthService);
const respObj =new BehaviorSubject( new HttpErrorResponse({
error: { errorMessage: "Invalid credentials"}
}));
component.email = 'vadine.asyw@infas.com';
component.password = 'pasywdja';
fixture.detectChanges();
spyOn(component, 'submitForm').and.callThrough();
spyOn(authService, 'login').and.returnValue(of(respObj));
component.submitForm();
fixture.detectChanges();
fixture.whenStable().then( () => {
expect(component.submitForm).toHaveBeenCalled();
expect(authService.login).toHaveBeenCalled();
let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
expect(msgElem).not.toBeNull();
console.log("text verify"+msgElem.innerHTML)
expect(msgElem.innerHTML).toContain("Invalid credentials")
})
}));
it('route to home when valid login', async(() => {
//routing to be tested when valid service response is
{jWT_Token: "y87y21skj.dh8712haskhdaksdj.haasdyusd", status:200}
}));
});
login.compoonent.html
<div class="container md-6">
<div class="container md-6">
<div class="container md-6">
<h1> Employee Management System</h1>
<form (ngSubmit)= 'submitForm()' #loginForm='ngForm'>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email"
name='email' [(ngModel)] ='email' required pattern = "[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$" #useremail="ngModel" >
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password" name='password' required [(ngModel)] = 'password' #loginPassword="ngModel">
</div>
<div *ngIf="(useremail.invalid && (useremail.dirty || useremail.touched)) || ( loginPassword.touched && loginPassword.invalid)" id="invalidDetails" class="alert alert-danger" role="alert">
Please Enter valid details.
</div>
<div *ngIf="message" id="message" class="alert alert-danger" role="alert">
{{message}}
</div>
<button type="submit" class="btn btn-primary d-flex justify-content-center" [disabled] = '!loginForm.form.valid'>Submit</button>
</form>
</div>
</div>
</div>
非常感谢任何帮助或建议。
你几乎成功了。
进行以下更改,希望您可以顺利进行(按照评论):
it('invalid user access', async(() => {
// get rid of 1 of the 2 lines here. They both do the same thing
// I got rid of the first one
// authService = fixture.debugElement.injector.get(AuthService);
authService = TestBed.get(AuthService);
// this should not have BehaviorSubject (it should just be an object)
const respObj = new HttpErrorResponse({
error: { errorMessage: "Invalid credentials"}
});
component.email = 'vadine.asyw@infas.com';
component.password = 'pasywdja';
fixture.detectChanges();
spyOn(component, 'submitForm').and.callThrough();
// this should not be of, but it should be throwError
// use of for successful observable
// import { throwError } from 'rxjs';
spyOn(authService, 'login').and.returnValue(throwError(respObj));
component.submitForm();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.submitForm).toHaveBeenCalled();
expect(authService.login).toHaveBeenCalled();
let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
expect(msgElem).not.toBeNull();
console.log("text verify"+msgElem.innerHTML)
expect(msgElem.innerHTML).toContain("Invalid credentials");
})
}));
我觉得可以帮你测试失败路由^.
要测试成功路由,是一样的,只是不用throwError
,用of
.
我是编写 Jasmine 测试用例的新手。我已经创建了 LoginComponent,然后它将使用 AuthService 检查服务器和 return 根据我将决定并适当显示信息的响应的 observable。
作为为此组件编写单元测试用例的一部分,我需要测试有效用户和无效用户登录。
场景一:登录无效
登录方法调用 AuthService.login() 将被订阅,如果错误消息将显示在登录页面上。
场景 2:有效登录 在这里,如果 resp 包含一些令牌,我们将认为它已通过身份验证并路由到主屏幕。
我尝试检查无效场景和面临的问题。
login.component.ts
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '../service/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
constructor(private authService: AuthService, private router:Router, private activeRoute:ActivatedRoute) { }
email:string;
password:string;
message:string;
ngOnInit() {
}
submitForm()
{
this.authService.login({username: this.email, password: this.password}).subscribe
(
(resp) =>
{
if(resp['status'] == 200 )
{
sessionStorage.setItem('UserAuthenticated', 'true');
sessionStorage.setItem('Jwt_Token', resp['JWT_Token']);
this.router.navigate(['/dashboard'])
}
},
(errorResp: HttpErrorResponse) =>
{
console.log(errorResp)
this.message = errorResp['error']['errorMessage'];
}
);
}
}
auth.service.ts
import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { environment } from 'src/environments/environment';
import { URLMapping } from '../shared/URLMapping';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) { }
login(data:object)
{
return this.http.post(environment.applicationURL+URLMapping.LOGIN, data);
}
}
login.component.spec.ts
import { HttpClientModule, HttpErrorResponse } from '@angular/common/http';
import { DebugElement } from '@angular/core';
import { async, ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { AuthService } from '../service/auth.service';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let authService: AuthService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginComponent ],
imports: [FormsModule, HttpClientModule, RouterTestingModule],
providers: [AuthService]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
fixture.autoDetectChanges();
});
it('invalid user access', async(() => {
authService = fixture.debugElement.injector.get(AuthService);
authService = TestBed.get(AuthService);
const respObj =new BehaviorSubject( new HttpErrorResponse({
error: { errorMessage: "Invalid credentials"}
}));
component.email = 'vadine.asyw@infas.com';
component.password = 'pasywdja';
fixture.detectChanges();
spyOn(component, 'submitForm').and.callThrough();
spyOn(authService, 'login').and.returnValue(of(respObj));
component.submitForm();
fixture.detectChanges();
fixture.whenStable().then( () => {
expect(component.submitForm).toHaveBeenCalled();
expect(authService.login).toHaveBeenCalled();
let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
expect(msgElem).not.toBeNull();
console.log("text verify"+msgElem.innerHTML)
expect(msgElem.innerHTML).toContain("Invalid credentials")
})
}));
it('route to home when valid login', async(() => {
//routing to be tested when valid service response is
{jWT_Token: "y87y21skj.dh8712haskhdaksdj.haasdyusd", status:200}
}));
});
login.compoonent.html
<div class="container md-6">
<div class="container md-6">
<div class="container md-6">
<h1> Employee Management System</h1>
<form (ngSubmit)= 'submitForm()' #loginForm='ngForm'>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email"
name='email' [(ngModel)] ='email' required pattern = "[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$" #useremail="ngModel" >
<small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" id="password" placeholder="Password" name='password' required [(ngModel)] = 'password' #loginPassword="ngModel">
</div>
<div *ngIf="(useremail.invalid && (useremail.dirty || useremail.touched)) || ( loginPassword.touched && loginPassword.invalid)" id="invalidDetails" class="alert alert-danger" role="alert">
Please Enter valid details.
</div>
<div *ngIf="message" id="message" class="alert alert-danger" role="alert">
{{message}}
</div>
<button type="submit" class="btn btn-primary d-flex justify-content-center" [disabled] = '!loginForm.form.valid'>Submit</button>
</form>
</div>
</div>
</div>
非常感谢任何帮助或建议。
你几乎成功了。
进行以下更改,希望您可以顺利进行(按照评论):
it('invalid user access', async(() => {
// get rid of 1 of the 2 lines here. They both do the same thing
// I got rid of the first one
// authService = fixture.debugElement.injector.get(AuthService);
authService = TestBed.get(AuthService);
// this should not have BehaviorSubject (it should just be an object)
const respObj = new HttpErrorResponse({
error: { errorMessage: "Invalid credentials"}
});
component.email = 'vadine.asyw@infas.com';
component.password = 'pasywdja';
fixture.detectChanges();
spyOn(component, 'submitForm').and.callThrough();
// this should not be of, but it should be throwError
// use of for successful observable
// import { throwError } from 'rxjs';
spyOn(authService, 'login').and.returnValue(throwError(respObj));
component.submitForm();
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component.submitForm).toHaveBeenCalled();
expect(authService.login).toHaveBeenCalled();
let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
expect(msgElem).not.toBeNull();
console.log("text verify"+msgElem.innerHTML)
expect(msgElem.innerHTML).toContain("Invalid credentials");
})
}));
我觉得可以帮你测试失败路由^.
要测试成功路由,是一样的,只是不用throwError
,用of
.