使用 react-hooks-testing-library 测试自定义钩子会抛出错误
Testing custom hook with react-hooks-testing-library throws an error
我正在尝试测试一个使用 axios 获取一些数据的简单挂钩。然而,测试抛出一个 TypeError:"Cannot read property 'fetchCompanies' of undefined"。这是我的自定义挂钩 (the full repo is here):
import { useState, useEffect } from 'react';
import { Company } from '../../models';
import { CompanyService } from '../../services';
export const useCompanyList = (): {
loading: boolean;
error: any;
companies: Array<Company>;
} => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const [companies, setCompanies] = useState<Array<Company>>([]);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const companies = await CompanyService.fetchCompanies();
// Sort by ticker
companies.sort((a, b) => {
if (a.ticker < b.ticker) return -1;
if (a.ticker > b.ticker) return 1;
return 0;
});
setCompanies(companies);
setLoading(false);
} catch (e) {
setError(e);
}
};
fetchData();
}, []);
return { loading, error, companies };
};
这是我的测试:
import { renderHook } from 'react-hooks-testing-library';
import { useCompanyList } from './useCompanyList';
const companiesSorted = [
{
ticker: 'AAPL',
name: 'Apple Inc.'
},
...
];
jest.mock('../../services/CompanyService', () => {
const companiesUnsorted = [
{
ticker: 'MSFT',
name: 'Microsoft Corporation'
},
...
];
return {
fetchCompanies: () => companiesUnsorted
};
});
describe('useCompanyList', () => {
it('returns a sorted list of companies', () => {
const { result } = renderHook(() => useCompanyList());
expect(result.current.loading).toBe(true);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual(companiesSorted);
});
});
请帮助我了解在这种情况下如何使用 react-hooks-testing-library。
编辑
这似乎与看似已解决的 Jest 问题有关。请参阅 https://github.com/facebook/jest/pull/3209。
TypeError: "Cannot read property 'fetchCompanies' of undefined"
是由您定义 CompanyService
服务的方式引起的。在代码中,您将导出一个包含所有服务方法的对象 CompanyService
。但是在您的测试中,您正在将 CompanyService
模拟为 return 具有方法的对象。
因此,模拟应该 return 一个 CompanyService
对象,它是一个具有所有方法的对象:
jest.mock('../../services/CompanyService', () => {
const companiesUnsorted = [
{
ticker: 'MSFT',
name: 'Microsoft Corporation'
},
...
];
return {
CompanyService: {
fetchCompanies: () => companiesUnsorted
}
};
});
现在,一旦你解决了这个问题,你会发现你不再有 TypeError
但你的测试没有通过。那是因为您要测试的代码是异步的,但您的测试不是。所以,在你渲染你的钩子之后(通过 renderHook
),result.current.companies
将是一个空数组。
您将不得不等待您的承诺得到解决。幸运的是,react-hooks-testing-library
为我们提供了一个 waitForNextUpdate
函数,以便等待下一次钩子更新。因此,测试的最终代码如下:
it('returns a sorted list of companies', async () => {
const { result, waitForNextUpdate } = renderHook(() => useCompanyList());
expect(result.current.loading).toBe(true);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual([]);
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual(companiesSorted);
});
我正在尝试测试一个使用 axios 获取一些数据的简单挂钩。然而,测试抛出一个 TypeError:"Cannot read property 'fetchCompanies' of undefined"。这是我的自定义挂钩 (the full repo is here):
import { useState, useEffect } from 'react';
import { Company } from '../../models';
import { CompanyService } from '../../services';
export const useCompanyList = (): {
loading: boolean;
error: any;
companies: Array<Company>;
} => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState();
const [companies, setCompanies] = useState<Array<Company>>([]);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const companies = await CompanyService.fetchCompanies();
// Sort by ticker
companies.sort((a, b) => {
if (a.ticker < b.ticker) return -1;
if (a.ticker > b.ticker) return 1;
return 0;
});
setCompanies(companies);
setLoading(false);
} catch (e) {
setError(e);
}
};
fetchData();
}, []);
return { loading, error, companies };
};
这是我的测试:
import { renderHook } from 'react-hooks-testing-library';
import { useCompanyList } from './useCompanyList';
const companiesSorted = [
{
ticker: 'AAPL',
name: 'Apple Inc.'
},
...
];
jest.mock('../../services/CompanyService', () => {
const companiesUnsorted = [
{
ticker: 'MSFT',
name: 'Microsoft Corporation'
},
...
];
return {
fetchCompanies: () => companiesUnsorted
};
});
describe('useCompanyList', () => {
it('returns a sorted list of companies', () => {
const { result } = renderHook(() => useCompanyList());
expect(result.current.loading).toBe(true);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual(companiesSorted);
});
});
请帮助我了解在这种情况下如何使用 react-hooks-testing-library。
编辑
这似乎与看似已解决的 Jest 问题有关。请参阅 https://github.com/facebook/jest/pull/3209。
TypeError: "Cannot read property 'fetchCompanies' of undefined"
是由您定义 CompanyService
服务的方式引起的。在代码中,您将导出一个包含所有服务方法的对象 CompanyService
。但是在您的测试中,您正在将 CompanyService
模拟为 return 具有方法的对象。
因此,模拟应该 return 一个 CompanyService
对象,它是一个具有所有方法的对象:
jest.mock('../../services/CompanyService', () => {
const companiesUnsorted = [
{
ticker: 'MSFT',
name: 'Microsoft Corporation'
},
...
];
return {
CompanyService: {
fetchCompanies: () => companiesUnsorted
}
};
});
现在,一旦你解决了这个问题,你会发现你不再有 TypeError
但你的测试没有通过。那是因为您要测试的代码是异步的,但您的测试不是。所以,在你渲染你的钩子之后(通过 renderHook
),result.current.companies
将是一个空数组。
您将不得不等待您的承诺得到解决。幸运的是,react-hooks-testing-library
为我们提供了一个 waitForNextUpdate
函数,以便等待下一次钩子更新。因此,测试的最终代码如下:
it('returns a sorted list of companies', async () => {
const { result, waitForNextUpdate } = renderHook(() => useCompanyList());
expect(result.current.loading).toBe(true);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual([]);
await waitForNextUpdate();
expect(result.current.loading).toBe(false);
expect(result.current.error).toBeUndefined();
expect(result.current.companies).toEqual(companiesSorted);
});