当我测试按钮是否被单击并在此测试通过的组件上调用onSignIn()方法时。
但是,当我尝试测试onSignIn()是否在AuthService上调用signIn()方法时,该测试未通过并通过网络调用来调用原始服务。
这是我的代码:
sign.in.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
import { AuthService } from '../../core';
import { AlertService, FormErrorHandler } from '../../shared';
@Component({
selector: 'app-sign-in',
templateUrl: './sign-in.component.html',
styleUrls: ['./sign-in.component.scss']
})
export class SignInComponent implements OnInit {
signInForm!: FormGroup;
...
constructor(
private formBuilder: FormBuilder,
private router: Router,
private activatedRoute: ActivatedRoute,
private authService: AuthService,
private alertService: AlertService
) {}
ngOnInit() {
...
}
...
onSignIn() {
...
this.authService
.signIn(this.signInForm.value)
.subscribe(
user => {
this.authService.setSession(user, this.rememberMe.value);
this.router.navigate([this.returnUrl]);
},
error => {
if (error instanceof Object) {
FormErrorHandler.errorHandler(this.signInForm, error);
} else {
this.alertService.error(error);
}
}
);
}
}
sign.in.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthRoutingModule } from './auth-routing.module';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { SharedModule } from '../shared';
import { AuthComponent } from './auth.component';
import { SignInComponent } from './sign-in/sign-in.component';
import { SignUpComponent } from './sign-up/sign-up.component';
import { PasswordRecoverComponent } from './password-recover/password-recover.component';
import { PasswordResetComponent } from './password-reset/password-reset.component';
@NgModule({
declarations: [
AuthComponent,
SignInComponent,
SignUpComponent,
PasswordRecoverComponent,
PasswordResetComponent
],
imports: [
CommonModule,
AuthRoutingModule,
SharedModule,
FontAwesomeModule,
ReactiveFormsModule,
FormsModule,
HttpClientModule,
]
})
export class AuthModule {}
auth.service.ts
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { map } from 'rxjs/operators';
import { config } from '../../../config';
import { User } from '../../../shared';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AuthService {
public isAuthenticated = false;
public authStatusSubject = new Subject<boolean>();
public currentUserId = '';
public currentUserToken = '';
get id() { return localStorage.getItem('id'); }
get token() { return localStorage.getItem('token'); }
get expiration() { return localStorage.getItem('expires_at'); }
getAuthStatusSubject() { return this.authStatusSubject.asObservable(); }
constructor(private http: HttpClient, private router: Router) {}
...
signIn(user: User) {
return this.http.post<{
message: string;
user: { id: string; token: string; expiresAt: string };
}>(`${config.URL}/api/auth/login`, {
email: user.email,
password: user.password
})
.pipe(map(data => { return data.user; }));
}
setSession(user, rememberMe: boolean) {
if (rememberMe) {
const expiresAt = new Date(user.expiresAt);
localStorage.setItem('id', user.id);
localStorage.setItem('token', user.token);
localStorage.setItem('expires_at', expiresAt.toUTCString());
}
this.isAuthenticated = true;
this.authStatusSubject.next(true);
this.currentUserId = user.id;
this.currentUserToken = user.token;
}
...
}
Author是在core.module中提供的
core.module.ts
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HeaderComponent } from './components';
import { AuthService, UserService } from './services';
@NgModule({
declarations: [ HeaderComponent ],
imports: [
CommonModule,
RouterModule,
FontAwesomeModule,
NgbModule,
],
providers: [
AuthService,
UserService
],
exports: [ HeaderComponent ]
})
export class CoreModule { }
sign.in.component.spec.ts
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AbstractControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { AuthService, CoreModule } from '../../core';
import { SharedModule } from '../../shared';
import { SignInComponent } from './sign-in.component';
describe('SignInComponent', () => {
let signInComponent: SignInComponent;
let fixture: ComponentFixture<SignInComponent>;
let signInButton: any;
let authService: AuthService;
...
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SignInComponent],
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
RouterTestingModule,
FontAwesomeModule,
CoreModule,
SharedModule
]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(SignInComponent);
signInComponent = fixture.componentInstance;
signInComponent.ngOnInit();
authService = TestBed.get(AuthService);
});
}));
beforeEach(() => {
fixture.detectChanges();
});
it('should be created', () => {
expect(signInComponent).toBeTruthy();
});
describe('#form', () => {
describe('#signInButton', () => {
beforeEach(() => {
signInButton = fixture.debugElement.query(By.css('button[type=submit]')).nativeElement;
});
it('should render button for submit form', () => {
expect(signInButton).toBeTruthy();
});
it('should sign in when submited', () => {
spyOn(signInComponent, 'onSignIn');
spyOn(authService, 'signIn');
signInForm.triggerEventHandler('submit', null);
expect(signInComponent.onSignIn).toHaveBeenCalled(); // This test pass
expect(authService.signIn).toHaveBeenCalled(); // This test fails and do a network call.
});
});
});
});