我正在嘗試在我的 Ionic 應用程式中使用 Angular 6、Jasmine 和 Karma 開始使用單元測驗。
我有一個 loginPage,它呼叫了一個 AuthService,而后者又呼叫了一個 HttpService,下面我將向您展示代碼。
我遇到的問題是,如果我呼叫 doLogin 函式(在 loginPage 中)或者如果我呼叫 login 函式(在 authService 中),我永遠無法得到回應。我嘗試使用訂閱并執行 toPromise() 將 Observable 轉換為 Promise,但我也沒有成功。
你能幫助我嗎?非常感謝。
登錄頁面.TS
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Credentials } from '../models/credentials';
import { AuthService } from '../services/auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.page.html',
styleUrls: ['./login.page.scss'],
})
export class LoginPage implements OnInit {
ionicForm: FormGroup;
isSubmitted: boolean = false;
credential: Credentials;
constructor(public formBuilder: FormBuilder, private _authService: AuthService) { }
ngOnInit() {
this.ionicForm = this.formBuilder.group({
email: ['[email protected]', [Validators.required, Validators.minLength(8)]],
password: ['cityslicka', [Validators.required, Validators.minLength(8)]],
});
}
doLogin1() {
this.isSubmitted = true;
if (!this.ionicForm.valid) {
return false;
} else {
return true
}
}
async doLogin(): Promise<Credentials | false> {
this.isSubmitted = true;
if (!this.ionicForm.valid) {
return false;
} else {
this.credential = await this._authService.login(this.ionicForm.value);
return this.credential;
}
}
get errorControl() {
return this.ionicForm.controls;
}
}
AUTHSERVICE.TS
import { Injectable } from '@angular/core';
import { HttpService } from './http.service';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private _httpService: HttpService) { }
async login(body){
return await this._httpService.post('https://reqres.in/api/login',body).toPromise();
}
}
HTTPSERVICE.TS
import { Injectable } from '@angular/core';
import { Observable, Subject, throwError } from 'rxjs';
import { HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http';
import { catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class HttpService {
errorLogin = new Subject<boolean>();
constructor(private httpClient: HttpClient) {
}
public get(path: string, options?: any): Observable<any> {
return this.httpClient.get(path,options).pipe(catchError(this.formatErrors));
}
public put(path: string, body: object = {},headers?:any): Observable<any> {
let options = {
headers: headers
}
return this.httpClient.put(path, JSON.stringify(body),options).pipe(catchError(this.formatErrors));
}
public post(path: string, body: object = {},options?: any): Observable<any> {
return this.httpClient.post(path,body,options).pipe(catchError(this.formatErrors));
}
public delete(path: string, headers?:any): Observable<any> {
return this.httpClient.delete(path, {headers,responseType: 'text' }).pipe(catchError(this.formatErrors));
}
public appendHeaders(header:HttpHeaders){
if (header != undefined){
return header.set('Content-Type','application/json');
}
return new HttpHeaders().set('Content-Type','application/json');
}
public formatErrors(error: HttpErrorResponse): Observable<any> {
if (error.status === 401 && error.error.message == 'Authentication failed'){
try {
this.errorLogin.next(true);
} catch(error){
}
}
return throwError(error);
}
showCredError(){
this.errorLogin.next(true);
}
hideCredError(){
this.errorLogin.next(false);
}
/*
Metodo de prueba, eliminar al pasar a producción
*/
public testRest(){
return this.httpClient.get('https://jsonplaceholder.typicode.com/todos/1');
}
}
LOGINPAGE.SPEC.TS
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { IonicModule } from '@ionic/angular';
import { HttpClientTestingModule, } from '@angular/common/http/testing';
import { LoginPage } from './login.page';
import { AuthService } from '../services/auth.service';
import { HttpService } from '../services/http.service';
import { of } from 'rxjs';
import { Credentials } from '../models/credentials';
describe('LoginPage', () => {
let component: LoginPage;
let fixture: ComponentFixture<LoginPage>;
let authService: AuthService;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ LoginPage ],
imports: [IonicModule.forRoot(),
FormsModule,
ReactiveFormsModule,
HttpClientTestingModule],
providers: [AuthService, HttpService]
}).compileComponents();
fixture = TestBed.createComponent(LoginPage);
component = fixture.componentInstance;
fixture.detectChanges();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
}));
it('A) should create', () => {
expect(component).toBeTruthy();
});
it('B) send form without data', () =>{
component.ionicForm = component.formBuilder.group({
email: ['', [Validators.required, Validators.minLength(8)]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
expect(data).toBeFalse();
});
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.errorUsernameRequired'))).not.toBeNull();
expect(fixture.debugElement.query(By.css('.errorPasswordRequired'))).not.toBeNull();
});
it('C) send form without password', () =>{
component.ionicForm = component.formBuilder.group({
email: ['UsuarioPrueba', [Validators.required, Validators.minLength(8)]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
expect(data).toBeFalse();
});
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.errorUsernameRequired'))).toBeNull();
expect(fixture.debugElement.query(By.css('.errorPasswordRequired'))).not.toBeNull();
});
it('D) send form without username', () =>{
component.ionicForm = component.formBuilder.group({
email: ['', [Validators.required, Validators.minLength(8)]],
password: ['Contrase?aPrueba', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
expect(data).toBeFalse();
});
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.errorUsernameRequired'))).not.toBeNull();
expect(fixture.debugElement.query(By.css('.errorPasswordRequired'))).toBeNull();
});
it('E) send form with minor lenght in both inputs', () =>{
component.ionicForm = component.formBuilder.group({
email: ['ABCDEF', [Validators.required, Validators.minLength(8)]],
password: ['ABCDEF', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
expect(data).toBeFalse();
});
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.errorPasswordMinLength'))).not.toBeNull();
expect(fixture.debugElement.query(By.css('.errorUsernameMinLength'))).not.toBeNull();
});
it('F) send form successfully', (()=>{
component.ionicForm = component.formBuilder.group({
email: ['[email protected]', [Validators.required, Validators.minLength(8)]],
password: ['cityslicka', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
console.log(data);
// THIS CONSOLE LOG NOT APPEAR IN THE CHROME CONSOLE
})
}));
});
uj5u.com熱心網友回復:
我會放棄HttpClientTestingModule并直接嘲笑AuthService。
遵循 // !! 的行:
describe('LoginPage', () => {
let component: LoginPage;
let fixture: ComponentFixture<LoginPage>;
// !! change this line to mockAuthService
let mockAuthService: jasmine.SpyObj<AuthService>;
beforeEach(waitForAsync(() => {
// !! assign mockAuthService to mock here
// !! first string argument is optional, 2nd array of strings are public methods you would like to mock
mockAuthService = jasmine.createSpyObj<AuthService>('AuthService', ['login']);
TestBed.configureTestingModule({
declarations: [ LoginPage ],
imports: [IonicModule.forRoot(),
FormsModule,
ReactiveFormsModule],
// !! provide mock for the real AuthService
providers: [{ provide: AuthService, useValue: mockAuthService }]
}).compileComponents();
fixture = TestBed.createComponent(LoginPage);
component = fixture.componentInstance;
fixture.detectChanges();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
}));
// !! you have an extra bracket after the ', and I think this could be a problem
it('F) send form successfully', ()=>{
component.ionicForm = component.formBuilder.group({
email: ['[email protected]', [Validators.required, Validators.minLength(8)]],
password: ['cityslicka', [Validators.required, Validators.minLength(8)]],
});
mockAuthService.login.and.returnValue(Promise.resolve({/* mock login respond here */}));
// !! below should hopefully work now
component.doLogin().then((data)=>{
console.log(data);
// THIS CONSOLE LOG NOT APPEAR IN THE CHROME CONSOLE
})
})
以上是單元測驗。單元測驗的心態應該假設其他一切正常,我的組件/類/等等。做它應該做的事?閱讀本文以模擬外部依賴項。
您的目標是進行集成測驗(提供實際的 AuthService),我認為我們也可以做到。我們必須查看佇列中的 http 請求并做出回應。
describe('LoginPage', () => {
let component: LoginPage;
let fixture: ComponentFixture<LoginPage>;
let authService: AuthService;
// !! get a handle on httpTestingController
let httpTestingController: HttpTestingController;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ LoginPage ],
imports: [IonicModule.forRoot(),
FormsModule,
ReactiveFormsModule,
HttpClientTestingModule],
providers: [AuthService, HttpService]
}).compileComponents();
fixture = TestBed.createComponent(LoginPage);
component = fixture.componentInstance;
// !! get handle, if you're using Angular 9 , get should be inject
httpTestingController = TestBed.get(HttpTestingController);
fixture.detectChanges();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
}));
// !! remove the ( after ',
it('F) send form successfully', () => {
component.ionicForm = component.formBuilder.group({
email: ['[email protected]', [Validators.required, Validators.minLength(8)]],
password: ['cityslicka', [Validators.required, Validators.minLength(8)]],
});
component.doLogin().then((data)=>{
console.log(data);
// THIS CONSOLE LOG NOT APPEAR IN THE CHROME CONSOLE
});
// !! get a handle on request
const request = httpTestingController.expectOne(request => request.url.includes('login'));
request.flush({/* flush what you would like for data to be logged */});
});
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/455110.html
下一篇:目錄的MacOS路徑格式
