【问题标题】:How do I test `image.onload` using jest in the context of redux actions (or other callbacks assigned in the action)如何在 redux 操作(或操作中分配的其他回调)的上下文中使用 jest 测试`image.onload`
【发布时间】:2018-04-26 16:38:16
【问题描述】:

我的问题是我试图对一个函数进行单元测试,但不知道如何测试它的一部分。

这是一个 react / redux 操作,它执行以下操作:

1) 使用图像 url 检索 json 数据

2) 将图像加载到 Image 实例中并将其大小分派给 reducer(使用 Image.onload 加载图像时异步)

3) 将 fetch 完成的 dispatch 发送到 reducer

图像加载是异步发生的,所以当我尝试对其进行单元测试时,它不会被调用。此外,我不能只是模拟出来,因为图像实例是在函数中创建的......

这是我想要测试的代码(删除一些检查、分支逻辑和其他东西):

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 操作是否被广播?

【问题讨论】:

    标签: javascript unit-testing react-redux jestjs


    【解决方案1】:

    经过一番调查,我发现了一个非常有趣的 javascript 函数,可以解决我的问题。

    是这个:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

    以下是我使用 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 将继续像往常一样获取或设置,但也会保存在单元测试范围内设置为变量的值(在本例中为函数)。之后,您可以运行在测试验证步骤之前捕获的函数。

    陷阱 - 需要设置可配置 - 请注意definePropertydefineProperties 的功能不同 - 这在实际代码中是不好的做法。 - 记得使用原型

    希望这篇文章可以帮助有需要的开发者!

    【讨论】:

    • 我调整了您的解决方案来模拟 src 的设置器,这对于测试错误和负载条件都很有效。我的解决方案(在别人的问题的背景下)可以在这里找到:stackoverflow.com/a/49204336/1601953
    猜你喜欢
    • 1970-01-01
    • 2018-06-18
    • 2017-05-22
    • 2018-09-14
    • 2016-08-17
    • 1970-01-01
    • 2019-08-30
    • 2017-10-19
    • 2016-02-21
    相关资源
    最近更新 更多