如何在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)

查看:63
本文介绍了如何在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)使用图片网址检索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)调度将获取操作完成给简化器

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

我在这里所做的是使用define属性覆盖Image的任何实例的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.

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

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天全站免登陆