在功能上测试模拟服务 then/catch - Angular/Karma
Test mocked service then/catch on function - Angular/Karma
我的 Angular/Karma 测试存在覆盖问题。
我创建了一个具有 signUp() 函数的组件
angularFireAuthSignOutSpyObj 是来自组件 (Firebase Auth)
this.auth 的间谍
signUp() {
if (this.registrationForm.valid) {
this.auth.createUserWithEmailAndPassword
(
this.registrationForm.get('email')?.value,
this.registrationForm.get('password')?.value
)
.then(() => {
this.appMessage = "Account created !";
})
.catch((error) => {
this.appMessage = error.message;
});
} else {
this.appMessage = 'Submit logic bypassed, form invalid !'
}
}
我正在用 karma 测试按原样测试这个组件功能
it('should submit registration with form values', () => {
spyOn(component, 'signUp').and.callThrough();
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue({
then: function () {
return {
catch: function () {
}
};
}
});
component.registrationForm.controls.email.setValue('test@email.com');
component.registrationForm.controls.password.setValue('ValidPass123');
component.registrationForm.controls.passwordCheck.setValue('ValidPass123');
expect(component.registrationForm.valid).toBeTruthy();
debugElement.query(By.css("button")).triggerEventHandler("click", null);
expect(component.signUp).toHaveBeenCalled();
expect(component.auth.createUserWithEmailAndPassword)
.toHaveBeenCalledWith(
component.registrationForm.controls.email.value,
component.registrationForm.controls.password.value)
// expect(component.appMessage).toEqual('Account created !');
});
你可以注意到最后一个 expect 被注释掉了,因为它 return 是一个 Error: Expected undefined to equal 'Account created !' .
这是因为即使 this.auth.createUserWithEmailAndPassword 在模拟服务 angularFireAuthSignOutSpyObj 中定义,并且使用 2 个预期参数正确调用,我无法控制定义的 then 和 catch 函数。
它们被定义为在 signUp() 函数中尝试访问它时不会触发错误。但是 我想做的 是触发 then(() => ...) 和 catch(() => ...) 这样我就可以 test/check app.message 已正确更新。
所有异常都有效,直到最后一个。我觉得我需要修改我的 createUserWithEmailAndPassword.and.returnValue 中的某些内容,以便可能 return 触发 then 或 catch 的内容。
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue({
then: function () {
return {
catch: function () {
}
};
}
});
有人知道我如何测试组件的实际 auth.createUserWithEmailAndPassword 结果行为吗?
非常感谢!
大卫
我没有看到你创建间谍的代码。您使用 promises 而不是 Observables 也有点奇怪。但是,我会研究监视方法——而不是 class,并返回一个你控制的承诺:
const resolveFunction;
const rejectFunction;
beforeEach(() => {
spyOn(component.auth, 'createUserWithEmailAndPassword').and.returnValue(new Promise((resolve, reject) => {
resolveFunction = resolve;
rejectFunction = reject;
})
}
现在,通过您的测试,您可以通过调用这些函数来控制 promise 何时被拒绝或解决:
it('test catch block', () => {
// some code
rejectFunction('some error object');
})
it('test then block', () => {
// some code
resolveFunction('some error object');
})
嘿,我只是想 post 进行更新,因为我设法做到了我所需要的。感谢@JeffryHouser 的提醒。
所以基本上我的组件最初期望来自查询的 Promise。如果结果恢复正常(UserCredentials),我们只需使用成功消息更新 appMessage 字符串。如果没有(捕获)我们 return 错误 消息 .
这些是我在测试端为了模拟解析所做的更改(promise 的正常结果,以及如何触发 catch)
- 使用 fakeAsync()
将测试设置为异步
- 监视用户 click() 使用的每个函数
- 指定 return 作为 angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword 函数的 Promise
- 用tick()
模拟异步流
- 使用 fixture.detectChanges()
检测承诺流结束后的变化
appMessage 项已按照流程正确更新
这里是代码!
间谍声明
let angularFireAuthSignOutSpyObj: jasmine.SpyObj<any>;
...
beforeEach(async () => {
angularFireAuthSignOutSpyObj = jasmine.createSpyObj('AngularFireAuth',
['createUserWithEmailAndPassword']);
...
});
用户凭据项
//Only setting the fields needed
export const testUserCredentials: UserCredential = {
user: {
providerData: [
{
email: 'test@email.com',
}
]
}
}
测试
it('should submit registration with form values', fakeAsync(() => {
spyOn(component, 'signUp').and.callThrough();
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.callFake(() => new Promise(
resolve => {
resolve(testUserCredentials);
})
);
component.registrationForm.controls.email.setValue('test@email.com');
component.registrationForm.controls.password.setValue('ValidPass123');
component.registrationForm.controls.passwordCheck.setValue('ValidPass123');
expect(component.registrationForm.valid).toBeTruthy();
debugElement.query(By.css("button")).triggerEventHandler("click", null);
expect(component.signUp).toHaveBeenCalled();
expect(component.auth.createUserWithEmailAndPassword)
.toHaveBeenCalledWith(
component.registrationForm.controls.email.value,
component.registrationForm.controls.password.value)
tick();
fixture.detectChanges();
expect(component.appMessage).toEqual('Account created : test@email.com');
}));
如何触发错误而不是解决
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.callFake(() => new Promise(() => {
throw {message: 'test purpose failure'};
}));
已更新register.component.ts
signUp() {
if (this.registrationForm.valid) {
let createdEmail: string | null | undefined;
this.auth.createUserWithEmailAndPassword
(
this.registrationForm.get('email')?.value,
this.registrationForm.get('password')?.value
)
.then((userCredential: UserCredential) => {
userCredential?.user?.providerData?.forEach(userInfo => {
createdEmail = userInfo?.email;
})
this.appMessage = "Account created : " + createdEmail;
})
.catch((error) => {
this.appMessage = "Account creation failed : " + error.message;
});
} else {
this.appMessage = 'Submit logic bypassed, form invalid !'
}
}
我的 Angular/Karma 测试存在覆盖问题。
我创建了一个具有 signUp() 函数的组件
angularFireAuthSignOutSpyObj 是来自组件 (Firebase Auth)
this.auth 的间谍 signUp() {
if (this.registrationForm.valid) {
this.auth.createUserWithEmailAndPassword
(
this.registrationForm.get('email')?.value,
this.registrationForm.get('password')?.value
)
.then(() => {
this.appMessage = "Account created !";
})
.catch((error) => {
this.appMessage = error.message;
});
} else {
this.appMessage = 'Submit logic bypassed, form invalid !'
}
}
我正在用 karma 测试按原样测试这个组件功能
it('should submit registration with form values', () => {
spyOn(component, 'signUp').and.callThrough();
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue({
then: function () {
return {
catch: function () {
}
};
}
});
component.registrationForm.controls.email.setValue('test@email.com');
component.registrationForm.controls.password.setValue('ValidPass123');
component.registrationForm.controls.passwordCheck.setValue('ValidPass123');
expect(component.registrationForm.valid).toBeTruthy();
debugElement.query(By.css("button")).triggerEventHandler("click", null);
expect(component.signUp).toHaveBeenCalled();
expect(component.auth.createUserWithEmailAndPassword)
.toHaveBeenCalledWith(
component.registrationForm.controls.email.value,
component.registrationForm.controls.password.value)
// expect(component.appMessage).toEqual('Account created !');
});
你可以注意到最后一个 expect 被注释掉了,因为它 return 是一个 Error: Expected undefined to equal 'Account created !' . 这是因为即使 this.auth.createUserWithEmailAndPassword 在模拟服务 angularFireAuthSignOutSpyObj 中定义,并且使用 2 个预期参数正确调用,我无法控制定义的 then 和 catch 函数。
它们被定义为在 signUp() 函数中尝试访问它时不会触发错误。但是 我想做的 是触发 then(() => ...) 和 catch(() => ...) 这样我就可以 test/check app.message 已正确更新。
所有异常都有效,直到最后一个。我觉得我需要修改我的 createUserWithEmailAndPassword.and.returnValue 中的某些内容,以便可能 return 触发 then 或 catch 的内容。
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.returnValue({
then: function () {
return {
catch: function () {
}
};
}
});
有人知道我如何测试组件的实际 auth.createUserWithEmailAndPassword 结果行为吗?
非常感谢!
大卫
我没有看到你创建间谍的代码。您使用 promises 而不是 Observables 也有点奇怪。但是,我会研究监视方法——而不是 class,并返回一个你控制的承诺:
const resolveFunction;
const rejectFunction;
beforeEach(() => {
spyOn(component.auth, 'createUserWithEmailAndPassword').and.returnValue(new Promise((resolve, reject) => {
resolveFunction = resolve;
rejectFunction = reject;
})
}
现在,通过您的测试,您可以通过调用这些函数来控制 promise 何时被拒绝或解决:
it('test catch block', () => {
// some code
rejectFunction('some error object');
})
it('test then block', () => {
// some code
resolveFunction('some error object');
})
嘿,我只是想 post 进行更新,因为我设法做到了我所需要的。感谢@JeffryHouser 的提醒。
所以基本上我的组件最初期望来自查询的 Promise。如果结果恢复正常(UserCredentials),我们只需使用成功消息更新 appMessage 字符串。如果没有(捕获)我们 return 错误 消息 .
这些是我在测试端为了模拟解析所做的更改(promise 的正常结果,以及如何触发 catch)
- 使用 fakeAsync() 将测试设置为异步
- 监视用户 click() 使用的每个函数
- 指定 return 作为 angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword 函数的 Promise
- 用tick() 模拟异步流
- 使用 fixture.detectChanges() 检测承诺流结束后的变化
appMessage 项已按照流程正确更新
这里是代码!
间谍声明
let angularFireAuthSignOutSpyObj: jasmine.SpyObj<any>;
...
beforeEach(async () => {
angularFireAuthSignOutSpyObj = jasmine.createSpyObj('AngularFireAuth',
['createUserWithEmailAndPassword']);
...
});
用户凭据项
//Only setting the fields needed
export const testUserCredentials: UserCredential = {
user: {
providerData: [
{
email: 'test@email.com',
}
]
}
}
测试
it('should submit registration with form values', fakeAsync(() => {
spyOn(component, 'signUp').and.callThrough();
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.callFake(() => new Promise(
resolve => {
resolve(testUserCredentials);
})
);
component.registrationForm.controls.email.setValue('test@email.com');
component.registrationForm.controls.password.setValue('ValidPass123');
component.registrationForm.controls.passwordCheck.setValue('ValidPass123');
expect(component.registrationForm.valid).toBeTruthy();
debugElement.query(By.css("button")).triggerEventHandler("click", null);
expect(component.signUp).toHaveBeenCalled();
expect(component.auth.createUserWithEmailAndPassword)
.toHaveBeenCalledWith(
component.registrationForm.controls.email.value,
component.registrationForm.controls.password.value)
tick();
fixture.detectChanges();
expect(component.appMessage).toEqual('Account created : test@email.com');
}));
如何触发错误而不是解决
angularFireAuthSignOutSpyObj.createUserWithEmailAndPassword.and.callFake(() => new Promise(() => {
throw {message: 'test purpose failure'};
}));
已更新register.component.ts
signUp() {
if (this.registrationForm.valid) {
let createdEmail: string | null | undefined;
this.auth.createUserWithEmailAndPassword
(
this.registrationForm.get('email')?.value,
this.registrationForm.get('password')?.value
)
.then((userCredential: UserCredential) => {
userCredential?.user?.providerData?.forEach(userInfo => {
createdEmail = userInfo?.email;
})
this.appMessage = "Account created : " + createdEmail;
})
.catch((error) => {
this.appMessage = "Account creation failed : " + error.message;
});
} else {
this.appMessage = 'Submit logic bypassed, form invalid !'
}
}