1

我有一个反应组件,它呈现一系列其他组件,每个组件都有自己的复选框。

有一个名为 rulesToDownload 的状态钩子,它以一个空数组开始,并在选中/取消选中复选框时将 id 添加到其中/从中删除。

当点击“下载”按钮时,rulesToDownload 数组被传递给数据函数 DownloadFundDataById,forEach 在数组上,并为每个值调用 window.open,并带有附加 id 的 api 调用。数据函数被导入到组件中,而不是作为道具传入。

这会导致多个选项卡在数据下载时关闭之前闪烁。它并不完美,但它有效。

我想完成我的测试覆盖,并且需要测试该函数是否在按钮单击时被调用,并且它应该做它应该做的事情。

任何帮助表示赞赏。

下面的代码:

摘要.test.js:

 it(`should create correct download array when some rules are selected`, async () => {
        global.open = sandbox.spy();

        fetch.mockResponseOnce(JSON.stringify(selectedRules));

        wrapper = mount(<Summary/>);
        await act(async () => {} );
        wrapper.update();

        wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});

        wrapper.find('button').first().simulate('click');
        expect(global.open).to.have.been.called();

    });

我可以确认所有“查找”语句都是正确的,并正确更新检查的值。

摘要.js:

const Summary = () => {
    const [expand, setExpand] = useState(false);
    const [buttonText, setButtonText] = useState("expand other rules");
    const [rulesToDownload, setRulesToDownload] = useState([]);
    const [data, setData] = useState([]);
    const [dataLoadComplete, setDataLoadComplete] = useState(false);
    const [dataLoadFailed, setDataLoadFailed] = useState(false);

    useEffect(() => {
            loadData();
    }, []);

    const loadData = async () => {
        try {
            let importedData = await ExecuteRules();
            setData(importedData);
            setDataLoadComplete(true);
        } catch (_) {
            setDataLoadFailed(true);
        }
    };

    const onButtonClick = () => {
        setExpand(!expand);
        if(!expand) setButtonText("hide other rules");
        else setButtonText("expand other rules");
    };

    const modifyDownloadArray = (id, checked) => {
        let tempArray;
        if(checked) tempArray = [...rulesToDownload, id];
        else tempArray = [...rulesToDownload.filter(ruleId => ruleId !== id)];
        setRulesToDownload([...tempArray]);
    };

    const dataFilter = (inputData, isFavouriteValue) => {
        return inputData.filter(rule => rule.isFavourite === isFavouriteValue)
                 .sort((a, b) => a.percentage - b.percentage)
                 .map((rule, i) => {
                    return <ReportProgressSummary
                                result={rule.percentage}
                                id={rule.id}
                                title={rule.name} key={i}
                                modifyDownloadArray={modifyDownloadArray}
                            />
                    })
    };

    return (
        <div className="test">
            {
                dataLoadFailed &&
                <div>Rule load failed</div>
            }
            {
                !dataLoadComplete &&
                    <LoadingSpinnerTitle holdingTitle="Loading rule data..."/>
            }
            {
                dataLoadComplete &&
                <Fragment>
                    <PageTitle title="System Overview"/>
                    <LineAndButtonContainerStyled>
                        <ContainerStyled>
                            {
                                dataFilter(data, true)
                            }
                        </ContainerStyled>
                        <ContainerStyled>
                            <ButtonStyled
                                    disabled={!rulesToDownload.length}
                                    onClick={() => DownloadFundDataById(rulesToDownload)}>
                                download
                            </ButtonStyled>
                        </ContainerStyled>
                    </LineAndButtonContainerStyled>

                    <LineBreakStyled/>

                    <ButtonStyled onClick={() => onButtonClick()}>{buttonText}</ButtonStyled>
                    {
                        expand &&
                        <ContainerStyled>
                            {
                                dataFilter(data, false)
                            }
                        </ContainerStyled>
                    }
                </Fragment>
            }
        </div>
    )
};

export default Summary;

数据方法.js:

export function DownloadFundDataById(downloadArray) {
    downloadArray.forEach(id => window.open(baseApiUrl + '/xxxx/xxxx/' + id));
}

我可以确认网址没问题,只是暂时更换

测试设置:

const doc = jsdom.jsdom('<!doctype html><html><body></body></html>')

global.document = doc;

global.window = doc.defaultView;

configure({ adapter: new Adapter() });

global.expect = expect;
global.sandbox = sinon.createSandbox();
global.React = React;
global.mount = mount;
global.shallow = shallow;
global.render = render;
global.fetch = jestFetchMock;
global.act = act;

chai.use(chaiAsPromised);
chai.use(sinonChai);
chai.use(chaiEnzyme());
chai.use(chaiJestDiff());

console.error = () => {};
console.warn = () => {};

当前的测试输出表明 global.open 没有被调用。我知道这是有道理的,因为它实际上并没有作为道具分配给按钮的 onClick 或任何东西。我认为这是我的问题之一 - 我不能直接为按钮分配存根,但我试图不重写我的代码以适应我的测试......

4

1 回答 1

1

设法通过对我的测试文件的一些更新来实现这一点:

it(`should create correct download array when some rules are selected`, async () => {
        global.open = sandbox.stub(window, "open");

        fetch.mockResponseOnce(JSON.stringify(selectedRules));

        wrapper = mount(<Summary/>);
        await act(async () => {} );
        wrapper.update();

        wrapper.find('ReportProgressSummary').first().find('input').last().simulate('change', {target: {checked: true}});

        wrapper.find('button').first().simulate('click');
        expect(global.open).to.have.been.called;

    });

sandbox.spy() 已更新为带有 (window, "open") 的 sandbox.stub()

感谢这篇文章的帮助!
https://github.com/mrdulin/mocha-chai-sinon-codelab/blob/master/src/stackoverflow/53524524/index.spec.js

此外,使用 to.be.call() 的期望语句实际上不是函数,因此已更新为 to.be.call

于 2020-02-03T13:49:07.157 回答