如何测试其他函数调用的异步函数?
How to test asynchronous functions called in other functions?
我的组件中有一个函数调用服务中的另一个异步函数,我无法测试订阅内部发生的情况。谁能帮我解决这个问题?
我需要知道是否调用了 this.auth.sendPasswordResetEmail (data.email) 函数。
login.component.ts
async forgotPassword() {
const {
["modules.organizations.sign_in.forgot_password.alert.title"]: title,
["modules.organizations.sign_in.forgot_password.alert.message"]: message,
["modules.organizations.sign_in.forgot_password.alert.message.success"]: success,
} = await this.translate.get([
"modules.organizations.sign_in.forgot_password.alert.title",
"modules.organizations.sign_in.forgot_password.alert.message",
"modules.organizations.sign_in.forgot_password.alert.message.success"
]).pipe(first()).toPromise()
this.alert.input(title, message).afterClosed()
.subscribe(async data => {
if(!data.ok) return
if(data.input == null) return
const loading = this.alert.loading()
try {
await this.auth.sendPasswordResetEmail(data.email)
loading.close()
this.alert.error(title, success)
} catch (error) {
loading.close()
this.alert.error(null, error)
}
})
}
在这部分我尝试做一些事情,但没有成功。
login.component.spec.ts
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let alertServiceMock: jasmine.SpyObj<any>;
let authServiceMock: jasmine.SpyObj<any>;
let dialogRefSpy: jasmine.SpyObj<any>;
let router: Router;
dialogRefSpy = jasmine.createSpy();
dialogRefSpy.component = {title: 'error', message: 'error'};
dialogRefSpy.afterClosed = () => of(true);
const matDialogSpy = jasmine.createSpyObj('MatDialog', [
'open',
'close',
]);
matDialogSpy.open.and.returnValue(dialogRefSpy);
matDialogSpy.close.and.returnValue(dialogRefSpy);
beforeEach(waitForAsync(() => {
authServiceMock = jasmine.createSpyObj('AuthService',[
'createUserWithEmailAndPassword',
'updateProfile',
'signInWithEmailAndPassword',
'signInWithPopup',
'sendPasswordResetEmail'
]);
authServiceMock.createUserWithEmailAndPassword.and.returnValue(true);
authServiceMock.updateProfile.and.returnValue(true);
authServiceMock.signInWithEmailAndPassword.and.returnValue(true);
authServiceMock.signInWithPopup.and.returnValue(true);
authServiceMock.sendPasswordResetEmail.and.returnValue(true);
alertServiceMock = jasmine.createSpyObj('AlertService',[
'message',
'error',
'input',
'password',
'loading',
'confirmation'
]);
alertServiceMock.error.and.returnValue(matDialogSpy.open(AlertComponent, {
data: {
type: 'error',
title: 'error',
error: 'description'
},
disableClose: true
}));
alertServiceMock.input.and.returnValue(matDialogSpy.open(AlertComponent, {
data: {
type: 'input',
title: 'title',
description: 'description'
},
disableClose: true
}));
alertServiceMock.loading.and.returnValue(matDialogSpy);
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule,
OverlayModule,
RouterTestingModule,
MatDialogModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateFakeLoader
}
})
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ LoginComponent ],
providers: [
{ provide: AlertService, useValue: alertServiceMock },
{ provide: AuthService, useValue: authServiceMock },
{ provide: MatDialog, useValue: matDialogSpy },
{ provide: ActivatedRoute, useFactory: () => mockActiveRoute },
MatSnackBar,
MatProgressBarModule,
FirestoreService,
FunctionsService,
TranslateService,
],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
fireStoreService = TestBed.inject(FirestoreService);
alertServiceMock = TestBed.inject(AlertService);
authServiceMock = TestBed.inject(AuthService);
functionsServiceMock = TestBed.inject(FunctionsService);
router = TestBed.inject(Router);
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
localStorage.removeItem('token');
fixture.destroy();
});
describe('tests on forgot password', () => {
it('should generate a FORGOT PASSWORD ALERT to enter an EMAIL', async () => {
await component.forgotPassword();
expect(authServiceMock.sendPasswordResetEmail).toHaveBeenCalled();
});
});
});
尝试添加日志并查看顺序。以下日志应该可以帮助您调试问题,我认为 fixture.whenStable
也会解决该问题。
这应该可以帮助您调试问题:
async forgotPassword() {
console.log('[Component] Forgot password was called');
const {
["modules.organizations.sign_in.forgot_password.alert.title"]: title,
["modules.organizations.sign_in.forgot_password.alert.message"]: message,
["modules.organizations.sign_in.forgot_password.alert.message.success"]: success,
} = await this.translate.get([
"modules.organizations.sign_in.forgot_password.alert.title",
"modules.organizations.sign_in.forgot_password.alert.message",
"modules.organizations.sign_in.forgot_password.alert.message.success"
]).pipe(first()).toPromise()
console.log('translate promise was resolved');
this.alert.input(title, message).afterClosed()
.subscribe(async data => {
console.log('[Component] inside of subscribe block');
if(!data.ok) return
if(data.input == null) return
const loading = this.alert.loading()
try {
console.log('[Component] calling sendPasswordResetEmail');
await this.auth.sendPasswordResetEmail(data.email)
console.log('[Component] sendPasswordResetEmail completed');
loading.close()
this.alert.error(title, success)
} catch (error) {
loading.close()
this.alert.error(null, error)
}
})
}
it('should generate a FORGOT PASSWORD ALERT to enter an EMAIL', async () => {
console.log('[Test] Calling forgotPassword');
await component.forgotPassword();
console.log('[Test] forgotPassword was called');
// I am thinking you need this await fixture.whenStable
// to wait for the promise of the async callback in the subscribe block
// to complete before making assertions.
await fixture.whenStable();
expect(authServiceMock.sendPasswordResetEmail).toHaveBeenCalled();
});
我的组件中有一个函数调用服务中的另一个异步函数,我无法测试订阅内部发生的情况。谁能帮我解决这个问题?
我需要知道是否调用了 this.auth.sendPasswordResetEmail (data.email) 函数。
login.component.ts
async forgotPassword() {
const {
["modules.organizations.sign_in.forgot_password.alert.title"]: title,
["modules.organizations.sign_in.forgot_password.alert.message"]: message,
["modules.organizations.sign_in.forgot_password.alert.message.success"]: success,
} = await this.translate.get([
"modules.organizations.sign_in.forgot_password.alert.title",
"modules.organizations.sign_in.forgot_password.alert.message",
"modules.organizations.sign_in.forgot_password.alert.message.success"
]).pipe(first()).toPromise()
this.alert.input(title, message).afterClosed()
.subscribe(async data => {
if(!data.ok) return
if(data.input == null) return
const loading = this.alert.loading()
try {
await this.auth.sendPasswordResetEmail(data.email)
loading.close()
this.alert.error(title, success)
} catch (error) {
loading.close()
this.alert.error(null, error)
}
})
}
在这部分我尝试做一些事情,但没有成功。
login.component.spec.ts
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let alertServiceMock: jasmine.SpyObj<any>;
let authServiceMock: jasmine.SpyObj<any>;
let dialogRefSpy: jasmine.SpyObj<any>;
let router: Router;
dialogRefSpy = jasmine.createSpy();
dialogRefSpy.component = {title: 'error', message: 'error'};
dialogRefSpy.afterClosed = () => of(true);
const matDialogSpy = jasmine.createSpyObj('MatDialog', [
'open',
'close',
]);
matDialogSpy.open.and.returnValue(dialogRefSpy);
matDialogSpy.close.and.returnValue(dialogRefSpy);
beforeEach(waitForAsync(() => {
authServiceMock = jasmine.createSpyObj('AuthService',[
'createUserWithEmailAndPassword',
'updateProfile',
'signInWithEmailAndPassword',
'signInWithPopup',
'sendPasswordResetEmail'
]);
authServiceMock.createUserWithEmailAndPassword.and.returnValue(true);
authServiceMock.updateProfile.and.returnValue(true);
authServiceMock.signInWithEmailAndPassword.and.returnValue(true);
authServiceMock.signInWithPopup.and.returnValue(true);
authServiceMock.sendPasswordResetEmail.and.returnValue(true);
alertServiceMock = jasmine.createSpyObj('AlertService',[
'message',
'error',
'input',
'password',
'loading',
'confirmation'
]);
alertServiceMock.error.and.returnValue(matDialogSpy.open(AlertComponent, {
data: {
type: 'error',
title: 'error',
error: 'description'
},
disableClose: true
}));
alertServiceMock.input.and.returnValue(matDialogSpy.open(AlertComponent, {
data: {
type: 'input',
title: 'title',
description: 'description'
},
disableClose: true
}));
alertServiceMock.loading.and.returnValue(matDialogSpy);
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
AngularFireModule.initializeApp(environment.firebase),
AngularFirestoreModule,
OverlayModule,
RouterTestingModule,
MatDialogModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateFakeLoader
}
})
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ LoginComponent ],
providers: [
{ provide: AlertService, useValue: alertServiceMock },
{ provide: AuthService, useValue: authServiceMock },
{ provide: MatDialog, useValue: matDialogSpy },
{ provide: ActivatedRoute, useFactory: () => mockActiveRoute },
MatSnackBar,
MatProgressBarModule,
FirestoreService,
FunctionsService,
TranslateService,
],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
fireStoreService = TestBed.inject(FirestoreService);
alertServiceMock = TestBed.inject(AlertService);
authServiceMock = TestBed.inject(AuthService);
functionsServiceMock = TestBed.inject(FunctionsService);
router = TestBed.inject(Router);
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
localStorage.removeItem('token');
fixture.destroy();
});
describe('tests on forgot password', () => {
it('should generate a FORGOT PASSWORD ALERT to enter an EMAIL', async () => {
await component.forgotPassword();
expect(authServiceMock.sendPasswordResetEmail).toHaveBeenCalled();
});
});
});
尝试添加日志并查看顺序。以下日志应该可以帮助您调试问题,我认为 fixture.whenStable
也会解决该问题。
这应该可以帮助您调试问题:
async forgotPassword() {
console.log('[Component] Forgot password was called');
const {
["modules.organizations.sign_in.forgot_password.alert.title"]: title,
["modules.organizations.sign_in.forgot_password.alert.message"]: message,
["modules.organizations.sign_in.forgot_password.alert.message.success"]: success,
} = await this.translate.get([
"modules.organizations.sign_in.forgot_password.alert.title",
"modules.organizations.sign_in.forgot_password.alert.message",
"modules.organizations.sign_in.forgot_password.alert.message.success"
]).pipe(first()).toPromise()
console.log('translate promise was resolved');
this.alert.input(title, message).afterClosed()
.subscribe(async data => {
console.log('[Component] inside of subscribe block');
if(!data.ok) return
if(data.input == null) return
const loading = this.alert.loading()
try {
console.log('[Component] calling sendPasswordResetEmail');
await this.auth.sendPasswordResetEmail(data.email)
console.log('[Component] sendPasswordResetEmail completed');
loading.close()
this.alert.error(title, success)
} catch (error) {
loading.close()
this.alert.error(null, error)
}
})
}
it('should generate a FORGOT PASSWORD ALERT to enter an EMAIL', async () => {
console.log('[Test] Calling forgotPassword');
await component.forgotPassword();
console.log('[Test] forgotPassword was called');
// I am thinking you need this await fixture.whenStable
// to wait for the promise of the async callback in the subscribe block
// to complete before making assertions.
await fixture.whenStable();
expect(authServiceMock.sendPasswordResetEmail).toHaveBeenCalled();
});