如何在 redux 动作(或动作中分配的其他回调)的上下文中使用 jest 测试 `image.onload` [英] How do I test `image.onload` using jest in the context of redux actions (or other callbacks assigned in the action)

查看:19
本文介绍了如何在 redux 动作(或动作中分配的其他回调)的上下文中使用 jest 测试 `image.onload`的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My problem was that I am trying to make a unit test for a function but can't figure out how to test a part of it.

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

This is a react / redux action that does the following:

1) 用图片 url 检索 json 数据

1) retrieves json data with an image url

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

2) loads the image into an Image instance and dispatches its size to the reducer (asynchronously when image is loaded using Image.onload)

3) 将 fetch 完成分派给 reducer

3) dispatches that the fetch was completed to the reducer

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

The image onload happens asynchronously, so when I try to unit test it it wouldn't be called. Moreover, I can't just mock things out because the image instance is created within the function...

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

Here's the code I wanted to test (removing some checks, branching logic, and stuff):

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.

Notice in that last storeImageSize private function how there's an instance of Image created and an image.onload that is assigned to a function.

现在这是我的测试:

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 回调被调用之前完成.

This test though will fail because the test finishes before the image.onload callback is called.

如何强制调用 image.onload 回调,以便测试`STORE_CARD_IMAGE_SIZE 操作是否被广播?

How can I force the image.onload callback to be called so that I can test that the `STORE_CARD_IMAGE_SIZE action gets broadcasted?

推荐答案

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

After some investigation, I found a very interesting javascript function that would solve my issue.

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

这是我如何使用 Object.defineProperty(...) 来解决我的问题:

Here's how I used Object.defineProperty(...) to solve my issue:

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,
        );
    });

我在这里所做的是使用定义属性来覆盖 Image 的任何实例的 setter.setter 将继续像往常一样获取或设置,但也会保存在单元测试范围内设置为变量的值(在本例中为函数).之后,您可以运行您在测试验证步骤之前捕获的那个函数.

What I did here was use define property to override the setter of any instance of Image. the setter would continue to get or set like normal but would also save the value (in this case a function) that was set to a variable in the scope of the unit test. After which, you can just run that function you captured before the verification step of your the test.

问题- 需要设置可配置- 注意 definePropertydefineProperties 是不同的函数- 这在实际代码中是不好的做法.- 记得使用原型

Gotchas - configurable needs to be set - note that defineProperty is a different function than defineProperties - This is bad practice in real code. - remember to use the prototype

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

Hope this post can help a dev in need!

这篇关于如何在 redux 动作(或动作中分配的其他回调)的上下文中使用 jest 测试 `image.onload`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆