使用 vitest 响应自定义钩子测试返回空结果

问题描述 投票:0回答:1

我创建了一个自定义挂钩,如下所示:

import { getUserInfo } from '@/scripts/services/userService';
import { useState, useEffect } from 'react';

const useFileFilter = (repositoryFiles: any[]) => {
    let enabledFilters: string[] = [];
    let disabledFilters: string[] = [];
    const [filteredRepositoryFiles, setFilteredRepositoryFiles] = useState([]);

    useEffect(() => {
        const filterFiles = async () => {
            let filteredFiles: object[] = [];
            const userInfo = await getUserInfo();

            for (const filter of userInfo.showSourceFiles) {
                if (filter.bool) {
                    enabledFilters.push(`.${filter.name}`);
                } else {
                    disabledFilters.push(`.${filter.name}`);
                }
            }
            if (enabledFilters.length === 0) {
                enabledFilters = disabledFilters;
                disabledFilters = [];
            }

            const fileExtentionsFilter = {
                include: enabledFilters,
                exclude: disabledFilters,
            };

            for (const file of repositoryFiles) {
                const included = fileExtentionsFilter.include.some(fileExtentions =>
                    file.name.endsWith(fileExtentions),
                );
                const excluded = fileExtentionsFilter.exclude.some(fileExtentions =>
                    file.name.endsWith(fileExtentions),
                );
                if (included && !excluded && !file.isDirectory) {
                    filteredFiles.push(file);
                }
            }
            console.log('works: ', filteredFiles);
            setFilteredRepositoryFiles(filteredFiles);
        }
        filterFiles();
    }, [repositoryFiles]);
    console.log('doesnt work: ', filteredRepositoryFiles);
    return filteredRepositoryFiles;
};

export default useFileFilter;

我正在尝试为它创建一个测试,如下所示:

import { describe, test, vi } from 'vitest';
import useFileFilter from '@/hooks/useFileFilter';
import { renderHook, waitFor } from '@testing-library/react';
import * as userService from '@/scripts/services/userService';

describe('For the useFileFilter hook', () => {
    const fetchSpy = vi.spyOn(userService, "getUserInfo");
    beforeAll(() => {
        const mockResolveValue = {
            "id": 1,
            "email": "[email protected]",
            "role": "user",
            "showSourceFiles": [
                {
                    "id": "cm4v6cj260003mpetpyicw2jm",
                    "name": "txt",
                    "bool": false,
                    "usersId": 1
                },
                {
                    "id": "cm4v6cj260004mpetznm7rpj6",
                    "name": "py",
                    "bool": false,
                    "usersId": 1
                },
                {
                    "id": "cm4v6cj260001mpet4z81kmcm",
                    "name": "gitignore",
                    "bool": true,
                    "usersId": 1
                },
                {
                    "id": "cm4v6cj260002mpetxe6csa1j",
                    "name": "yml",
                    "bool": true,
                    "usersId": 1
                }
            ],
            "firstName": null,
            "lastName": null,
            "avatarUrl": null,
            "githubUsername": "SomeUser",
            "githubAccessToken": "a_token"
        }
        fetchSpy.mockReturnValue(mockResolveValue as any);
    });

    afterAll(() => {
        fetchSpy.mockRestore();
    });

    it('should return filtered repository files', async () => {
        const repositoryFiles = [
            {
                "id": 1,
                "isDirectory": false,
                "path": ".gitignore",
                "name": ".gitignore",
                "lineCount": 161,
                "functionalLineCount": 80,
                "symlinkTarget": "",
                "branchId": 1
            },
            {
                "id": 2,
                "isDirectory": false,
                "path": "Dockerfile",
                "name": "Dockerfile",
                "lineCount": 8,
                "functionalLineCount": 7,
                "symlinkTarget": "",
                "branchId": 1
            },
            {
                "id": 3,
                "isDirectory": false,
                "path": "compose.yml",
                "name": "compose.yml",
                "lineCount": 18,
                "functionalLineCount": 17,
                "symlinkTarget": "",
                "branchId": 1
            },
            ...
        ]
        const result = renderHook(() => useFileFilter(repositoryFiles));

        console.log('result: ', result);
    });

当我运行测试时,它会产生空结果:

result:  {
  result: { current: [] },
  rerender: [Function: rerender],
  unmount: [Function: unmount]
}

请注意第一个代码块中的console.logs。他们产生以下结果:

doesnt work:  []
works:  [
  {
    id: 1,
    isDirectory: false,
    path: '.gitignore',
    name: '.gitignore',
    lineCount: 161,
    functionalLineCount: 80,
    symlinkTarget: '',
    branchId: 1
  },
  {
    id: 3,
    isDirectory: false,
    path: 'compose.yml',
    name: 'compose.yml',
    lineCount: 18,
    functionalLineCount: 17,
    symlinkTarget: '',
    branchId: 1
  }
]

因此,由于某种原因,测试运行良好,但是当它尝试使用

FilteredRepositoryFiles
更新
setFilteredRepositoryFiles
的状态时,状态保持为空。尽管在测试之外使用此功能时效果很好。有谁知道为什么吗?

PS:反应很新

reactjs vitest
1个回答
0
投票

你不应该像在 React 中那样操作数组。让我们参考一下文档。

请阅读:

您不需要效果来转换数据以进行渲染。例如, 假设您想在显示列表之前对其进行过滤。你可能会 很想编写一个 Effect 来更新状态变量,当 列表更改。然而,这是 效率低下...

在这种情况下,一个 useMemo 就足够了,但是,因为有一个 api 调用,所以我们必须使用 useCallback。由于我们需要调用 useCallback,我们还将使用 useState 来存储结果,并使用 useEffect 在需要时执行操作(当存储库文件更改时):

import { useCallback, useEffect, useMemo, useState } from 'react';
import { getUserInfo } from '@/scripts/services/userService';

const useFileFilter = (repositoryFiles: any[]) => {
    const [filteredRepositoryFiles, setFilteredRepositoryFiles] = useState();

    const filterRepositoryFiles = useCallback(async () => {
        let enabledFilters: string[] = [];
        let disabledFilters: string[] = [];
        let filteredFiles: object[] = [];
        const userInfo = await getUserInfo();

        for (const filter of userInfo.showSourceFiles) {
            if (filter.bool) {
                enabledFilters.push(`.${filter.name}`);
            } else {
                disabledFilters.push(`.${filter.name}`);
            }
        }
        if (enabledFilters.length === 0) {
            enabledFilters = disabledFilters;
            disabledFilters = [];
        }

        const fileExtentionsFilter = {
            include: enabledFilters,
            exclude: disabledFilters,
        };

        for (const file of repositoryFiles) {
            const included = fileExtentionsFilter.include.some(
                (fileExtentions) => file.name.endsWith(fileExtentions),
            );
            const excluded = fileExtentionsFilter.exclude.some(
                (fileExtentions) => file.name.endsWith(fileExtentions),
            );
            if (included && !excluded && !file.isDirectory) {
                filteredFiles.push(file);
            }
        }

        return filteredFiles;
    }, [repositoryFiles]);

    useEffect(() => {
        //Only call when repositoryFiles has content
        if (repositoryFiles.length > 0)
            filterRepositoryFiles().then((v) => setFilteredRepositoryFiles(v));
    }, [filterRepositoryFiles, repositoryFiles]);

    return filteredRepositoryFiles;
};

export default useFileFilter;
© www.soinside.com 2019 - 2024. All rights reserved.