我的问题是我正在尝试对某个功能进行单元测试,但无法弄清楚如何测试它的一部分。
这是一个反应/减少行动,执行以下操作:
1)使用图片网址
检索json数据2)将图像加载到Image实例中并将其大小调度到reducer(使用Image.onload加载图像时异步)
3)调度fetch已完成到reducer
图像onload是异步发生的,所以当我尝试对它进行单元测试时,它就不会被调用。而且,我不能嘲笑事情,因为图像实例是在函数中创建的......
这是我想要测试的代码(删除一些检查,分支逻辑和东西):
export function fetchInsuranceCardPhoto() {
return dispatch => {
dispatch(requestingInsuranceCardPhoto());
return fetch(`${api}`,
{
headers: {},
credentials: 'same-origin',
method: 'GET',
})
.then(response => {
switch (response.status) {
case 200:
return response.json()
.then(json => {
dispatch(receivedInsuranceCardPhoto(json));
})
}
});
};
}
function receivedInsuranceCardPhoto(json) {
return dispatch => {
const insuranceCardFrontImg = json.insuranceCardData.url_front;
const insuranceCardBackImg = json.insuranceCardData.url_back;
if (insuranceCardFrontImg) {
dispatch(storeImageSize(insuranceCardFrontImg, 'insuranceCardFront'));
}
return dispatch(receivedInsuranceCardPhotoSuccess(json));
};
}
function receivedInsuranceCardPhotoSuccess(json) {
const insuranceCardFrontImg = json.insuranceCardData.url_front;
const insuranceCardBackImg = json.insuranceCardData.url_back;
const insuranceCardId = json.insuranceCardData.id;
return {
type: RECEIVED_INSURANCE_CARD_PHOTO,
insuranceCardFrontImg,
insuranceCardBackImg,
insuranceCardId,
};
}
function storeImageSize(imgSrc, side) {
return dispatch => {
const img = new Image();
img.src = imgSrc;
img.onload = () => {
return dispatch({
type: STORE_CARD_IMAGE_SIZE,
side,
width: img.naturalWidth,
height: img.naturalHeight,
});
};
};
}
请注意,最后storeImageSize
个私函数是如何创建一个Image实例的,以及一个分配给函数的image.onload。
现在我的考试是:
it('triggers RECEIVED_INSURANCE_CARD_PHOTO when 200 returned without data', async () => {
givenAPICallSucceedsWithData();
await store.dispatch(fetchInsuranceCardPhoto());
expectActionsToHaveBeenTriggered(
REQUESTING_INSURANCE_CARD_PHOTO,
RECEIVED_INSURANCE_CARD_PHOTO,
STORE_CARD_IMAGE_SIZE,
);
});
此测试虽然会失败,因为测试在调用image.onload
回调之前完成。
如何强制调用image.onload
回调,以便我可以测试`STORE_CARD_IMAGE_SIZE操作是否被广播?
答案 0 :(得分:5)
经过一番调查,我发现了一个非常有趣的javascript函数,可以解决我的问题。
以下是我使用Object.defineProperty(...)
解决问题的方法:
describe('fetchInsuranceCardPhoto', () => {
let imageOnload = null;
/** Override Image global to save onload setting here so that I can trigger it manually in my test */
function trackImageOnload() {
Object.defineProperty(Image.prototype, 'onload', {
get: function () {
return this._onload;
},
set: function (fn) {
imageOnload = fn;
this._onload = fn;
},
});
}
it('triggers RECEIVED_INSURANCE_CARD_PHOTO when 200 returned with data', async () => {
trackImageOnload();
givenAPICallSucceedsWithData();
await store.dispatch(fetchInsuranceCardPhoto());
imageOnload();
expectActionsToHaveBeenTriggered(
REQUESTING_INSURANCE_CARD_PHOTO,
RECEIVED_INSURANCE_CARD_PHOTO,
STORE_CARD_IMAGE_SIZE,
);
});
我在这里做的是使用define属性覆盖Image
的任何实例的setter。 setter将继续像正常一样获取或设置,但也会保存设置为单元测试范围内的变量的值(在本例中为函数)。之后,您可以在测试的验证步骤之前运行您捕获的功能。
陷阱
- 可配置需要设置
- 请注意defineProperty
与defineProperties
的功能不同
- 这在实际代码中是不好的做法。
- 记得使用原型
希望这篇文章可以帮助开发人员!