在对 React 组件进行单元测试时,我需要模拟我的自定义钩子。我已经阅读了一些 stackoverflow 的答案,但没有成功地正确实现它。
我不能在不模拟的情况下使用 useAuth,因为它取决于服务器请求,而我目前只编写单元测试。
//useAuth.js - custom hook
import React, { createContext, useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
const authContext = createContext();
function useProvideAuth() {
const [accessToken, setAccessToken] = useState('');
const [isAuthenticated, setAuthenticated] = useState(
accessToken ? true : false
);
useEffect(() => {
refreshToken();
}, []);
const login = async (loginCredentials) => {
const accessToken = await sendLoginRequest(loginCredentials);
if (accessToken) {
setAccessToken(accessToken);
setAuthenticated(true);
}
};
const logout = async () => {
setAccessToken(null);
setAuthenticated(false);
await sendLogoutRequest();
};
const refreshToken = async () => {
const accessToken = await sendRefreshRequest();
if (accessToken) {
setAccessToken(accessToken);
setAuthenticated(true);
} else setAuthenticated(false);
setTimeout(async () => {
refreshToken();
}, 15 * 60000 - 1000);
};
return {
isAuthenticated,
accessToken,
login,
logout
};
}
export function AuthProvider({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
AuthProvider.propTypes = {
children: PropTypes.any
};
const useAuth = () => {
return useContext(authContext);
};
export default useAuth;
我写的测试
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import { NavBar } from '../App';
jest.resetAllMocks();
jest.mock('../auth/useAuth', () => {
const originalModule = jest.requireActual('../auth/useAuth');
return {
__esModule: true,
...originalModule,
default: () => ({
accessToken: 'token',
isAuthenticated: true,
login: jest.fn,
logout: jest.fn
})
};
});
describe('NavBar when isAuthenticated', () => {
it('LogOut button is visible when isAuthenticated', () => {
render(<NavBar />);
expect(screen.getByText(/log out/i)).toBeVisible();
});
});
我正在编写测试的功能:
//App.js
import React from 'react';
import cn from 'classnames';
import useAuth, { AuthProvider } from './auth/useAuth';
import './App.css';
import '../node_modules/bootstrap/dist/css/bootstrap.css';
function App() {
return (
<AuthProvider>
<Router>
<NavBar />
</Router>
</AuthProvider>
);
}
const NavBarSignUpButton = () => (
<button className='button info'>
Sign up
</button>
);
const NavBarLogoutButton = () => {
const auth = useAuth();
const handleLogOut = () => {
auth.logout();
};
return (
<button className='button info' onClick={handleLogOut}>
Log out
</button>
);
};
export const NavBar = () => {
const isAuthenticated = useAuth().isAuthenticated;
const loginButtonClassName = cn({
btn: true,
invisible: isAuthenticated
});
return (
<nav className='navbar navbar-expand-lg navbar-light'>
<div className='container'>
<div className='d-flex justify-content-end'>
<div className='navbar-nav'>
<button className={loginButtonClassName}>
Log In
</button>
{isAuthenticated ? <NavBarLogoutButton /> : <NavBarSignUpButton />}
</div>
</div>
</div>
</nav>
);
};
上面的测试代码不会抛出任何错误。但是,测试失败,因为 useAuth().isAuthenticated 始终为假(但我在模拟它以返回真)。无论我测试 App 还是仅测试 NavBar 都不会改变
我究竟做错了什么?