angular - 如何使用 auth.service.ts 为 login.component.ts 编写单元测试?
问题描述
我有我想测试的带有 submit() 函数的 login.component 。我怎样才能做到这一点?我读了很多文章,但我不明白如何在 login.component 的 submit() 方法中调用我的 auth.service
登录组件.ts
export class LoginComponent implements OnInit{
form: FormGroup;
error: string;
constructor(public authService: AuthService, private router: Router) {
}
ngOnInit(): void {
this.form = new FormGroup({
email: new FormControl('', [Validators.required, Validators.email]),
password: new FormControl('', [Validators.required, Validators.minLength(6)])
});
}
submit(): void {
if (this.form.invalid) {
return;
}
const user: User = {
email: this.form.value.email,
password: this.form.value.password,
};
this.authService.signIn(user)
.then(() => this.router.navigate(['/']))
.catch(err => this.error = err.message);
}
.....
}
auth.service.ts 这是我调用的 singIn() 方法
@Injectable(
{providedIn: 'root'}
)
export class AuthService {
user: User;
constructor(private angularFireAuth: AngularFireAuth,
private httpClient: HttpClient) {
}
signIn(user: User): Promise<any> {
return this.angularFireAuth.signInWithEmailAndPassword(user.email, user.password)
.then(result => {
return this.user = {
email: result.user.email,
uid: result.user.uid,
};
});
}
....
}
现在我有这样的“规范”文件来测试 login.component.spec.ts
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
class MockAuthService{
user: User;
signIn(user: User): Promise<any> {
return new Promise(resolve => resolve(this.user = {...user}));
}
}
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [RouterTestingModule, FormsModule, ReactiveFormsModule],
providers: [{provide: AuthService, useClass: MockAuthService}],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
});
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create component', () => {
expect(component).toBeTruthy();
});
it('should create form with 2 controls', () => {
expect(component.form.contains('email')).toBeTruthy();
expect(component.form.contains('password')).toBeTruthy();
});
it('should render error message', () => {
const errorMessage = 'Error';
component.error = errorMessage;
fixture.detectChanges();
const debug = fixture.debugElement.query(By.css('.form__error'));
const el: HTMLElement = debug.nativeElement.textContent;
expect(el).toContain(errorMessage);
});
it('component initial state', () => {
expect(component.submit).toBeDefined();
expect(component.googleAuth).toBeDefined();
expect(component.facebookAuth).toBeDefined();
expect(component.githubAuth).toBeDefined();
expect(component.form.invalid).toBeTruthy();
expect(component.error).toBeUndefined();
});
it('should be true when invalid form', () => {
component.form.controls['email'].setValue('check.point@');
component.form.controls['password'].setValue('231');
expect(component.form.invalid).toBeTruthy();
});
});
解决方案
我想你快到了。
// get rid of class MockAuthService, we will use spyObj instead
let mockAuthService: jasmine.SpyObj<AuthService>;
let router: Router;
beforeEach(async () => {
// the first argument is string is an identifier (optional) and the second argument is an array of strings of the public methods you would like to mock
mockAuthService = jasmine.createSpyObj<AuthService>('AuthService', ['signIn']);
await TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [RouterTestingModule, FormsModule, ReactiveFormsModule],
// change below line
providers: [{provide: AuthService, useValue: mockAuthService }],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
});
.compileComponents();
});
beforeEach(() => {
router = TestBed.inject(router); // inject is get if on version 9 and below of Angular
// spy on the navigate method so it doesn't actually want to navigate
spyOn(router, 'navigate');
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
...
it('should navigate on submit', async () => {
// set these values so the form is valid and the method does not return early
component.form['email'].setValue('test@test.com');
component.form['password'].setValue('1234567');
// for our testing we don't care what it returns, as long as it is a promise
mockAuthService.signIn.and.returnValue(Promise.resolve(undefined));
component.submit();
// wait for all pending promises to resolve
await fixture.whenStable();
expect(router.navigate).toHaveBeenCalledWith(['/]);
});
it('should set error on error', async () => {
// set these values so the form is valid and the method does not return early
component.form['email'].setValue('test@test.com');
component.form['password'].setValue('1234567');
// for our testing we don't care what it returns, as long as it is a promise
mockAuthService.signIn.and.returnValue(Promise.reject({ message: 'error' }));
component.submit();
// wait for all pending promises to resolve
await fixture.whenStable();
expect(component.error).toBe('error');
});
推荐阅读
- linux - 命令 '/bin/sh -c ssh-keyscan -t rsa 172.168.85.74 >> /root/.ssh/known_hosts' 返回一个非零代码:1
- angular - CORS 策略已阻止对 XMLHttpRequest 的访问:Spring API 和 Angular UI
- python - 如何在多个 python 项目之间共享一个通用的帮助文件?
- python - python:检查列表中的所有元素是否只是数字
- javascript - 如何禁用日期选择器中的某些日子
- git - 如何 git 取消提交某些文件但不丢失它们?
- jsf - ajax dateSelect 事件更新后 Primefaces 日期值未设置正确值
- ios - 使用核心数据保存 Json
- django - 我正在尝试将 Django 与 Celery、Celery-beat、Redis、Postgresql、Nginx、Gunicorn 与 Docker 部署到 Heroku
- python - 在pygame中设置图像的不透明度