在提供的上下文中调用而不是 Jest 模拟的实现函数
Implemented function in provided context called instead of Jest mock
我需要测试在调用上下文提供的函数的 React 应用程序中单击按钮。在 Jest 测试中调用上下文提供的模拟函数时,将调用实际函数。如何解决此问题?
申请App.tsx
import React, {useContext, useState} from 'react'
import styles from './App.module.scss'
import {ThemeContext} from './context/theme/ThemeProvider'
export const App = (): JSX.Element => {
const {changeTheme, activeTheme} = useContext(ThemeContext)
const [theme, setTheme] = useState<string>(activeTheme)
const setNewTheme = (newTheme: string) => {
changeTheme(newTheme)
setTheme(newTheme)
}
return (
<div className={styles.container}>
<h1>Project Name</h1>
<button
data-testid='app-theme-btn1'
onClick={() => {
setNewTheme('dark')
}}
>
Click here
</button>
<p>{`Active Theme: ${theme}`}</p>
</div>
)
}
实测
test('click button changes theme', async () => {
const defaultTheme = 'light'
type ThemeContext = {
activeTheme: string
changeTheme: (theme: string) => void
}
const defaultThemeContext: ThemeContext = {
activeTheme: defaultTheme,
changeTheme: (theme) => {
/* left empty */
}
}
const ThemeContext = createContext<ThemeContext>(defaultThemeContext)
const themeContext = {
activeTheme: defaultTheme,
changeTheme: jest.fn().mockImplementation((newTheme) => {
themeContext.activeTheme = newTheme
})
}
render(
<ThemeContext.Provider value={themeContext}>
<App />
</ThemeContext.Provider>
)
screen.debug(undefined, Infinity)
const themeButton = screen.getByTestId('app-theme-btn1')
await userEvent.click(themeButton)
await waitFor(() => screen.queryByText('Active Theme: dark'))
// Error: expect(jest.fn()).toHaveBeenCalledTimes(expected)
//
// Expected number of calls: 1
// Received number of calls: 0
expect(themeContext.changeTheme).toHaveBeenCalledTimes(1)
screen.debug(undefined, Infinity)
})
问题是您在测试用例中创建了不同的 React 上下文,而不是使用在 ./context/theme/ThemeProvider
模块中创建的 ThemeContext
。
也就是说useContext()
hook在App
组件中使用的context应该是ThemeContext
,这样App
组件才能接收context值并订阅值从 ThemeContext.Provider
.
变化
例如
app.tsx
:
import React, { useContext, useState } from 'react';
import { ThemeContext } from './theme-provider';
export const App = (): JSX.Element => {
const { changeTheme, activeTheme } = useContext(ThemeContext);
const [theme, setTheme] = useState<string>(activeTheme);
const setNewTheme = (newTheme: string) => {
changeTheme(newTheme);
setTheme(newTheme);
};
return (
<div>
<h1>Project Name</h1>
<button
data-testid="app-theme-btn1"
onClick={() => {
setNewTheme('dark');
}}
>
Click here
</button>
<p>{`Active Theme: ${theme}`}</p>
</div>
);
};
theme-provider.tsx
:
import React, { useState } from 'react';
const defaultContext = {
activeTheme: 'light',
changeTheme: (newTheme: string) => {},
};
export const ThemeContext = React.createContext(defaultContext);
export const ThemeProvider = ({ children }) => {
const [activeTheme, setActiveTheme] = useState(defaultContext.activeTheme);
const changeTheme = (newTheme: string) => {
setActiveTheme(newTheme);
};
return <ThemeContext.Provider value={{ activeTheme, changeTheme }}>{children}</ThemeContext.Provider>;
};
app.test.tsx
:
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { App } from './app';
import { ThemeContext } from './theme-provider';
describe('71901699', () => {
test('should pass', async () => {
const defaultTheme = 'light';
const themeContext = {
activeTheme: defaultTheme,
changeTheme: jest.fn().mockImplementation((newTheme) => {
themeContext.activeTheme = newTheme;
}),
};
render(
<ThemeContext.Provider value={themeContext}>
<App />
</ThemeContext.Provider>
);
const themeButton = screen.getByTestId('app-theme-btn1');
userEvent.click(themeButton);
expect(themeContext.changeTheme).toHaveBeenCalledTimes(1);
await waitFor(() => screen.queryByText('Active Theme: dark'));
});
});
测试结果:
PASS Whosebug/71901699/app.test.tsx (8.691 s)
71901699
✓ should pass (74 ms)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 80 | 100 | 50 | 77.78 |
app.tsx | 100 | 100 | 100 | 100 |
theme-provider.tsx | 55.56 | 100 | 0 | 50 | 10-14
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.217 s
我需要测试在调用上下文提供的函数的 React 应用程序中单击按钮。在 Jest 测试中调用上下文提供的模拟函数时,将调用实际函数。如何解决此问题?
申请App.tsx
import React, {useContext, useState} from 'react'
import styles from './App.module.scss'
import {ThemeContext} from './context/theme/ThemeProvider'
export const App = (): JSX.Element => {
const {changeTheme, activeTheme} = useContext(ThemeContext)
const [theme, setTheme] = useState<string>(activeTheme)
const setNewTheme = (newTheme: string) => {
changeTheme(newTheme)
setTheme(newTheme)
}
return (
<div className={styles.container}>
<h1>Project Name</h1>
<button
data-testid='app-theme-btn1'
onClick={() => {
setNewTheme('dark')
}}
>
Click here
</button>
<p>{`Active Theme: ${theme}`}</p>
</div>
)
}
实测
test('click button changes theme', async () => {
const defaultTheme = 'light'
type ThemeContext = {
activeTheme: string
changeTheme: (theme: string) => void
}
const defaultThemeContext: ThemeContext = {
activeTheme: defaultTheme,
changeTheme: (theme) => {
/* left empty */
}
}
const ThemeContext = createContext<ThemeContext>(defaultThemeContext)
const themeContext = {
activeTheme: defaultTheme,
changeTheme: jest.fn().mockImplementation((newTheme) => {
themeContext.activeTheme = newTheme
})
}
render(
<ThemeContext.Provider value={themeContext}>
<App />
</ThemeContext.Provider>
)
screen.debug(undefined, Infinity)
const themeButton = screen.getByTestId('app-theme-btn1')
await userEvent.click(themeButton)
await waitFor(() => screen.queryByText('Active Theme: dark'))
// Error: expect(jest.fn()).toHaveBeenCalledTimes(expected)
//
// Expected number of calls: 1
// Received number of calls: 0
expect(themeContext.changeTheme).toHaveBeenCalledTimes(1)
screen.debug(undefined, Infinity)
})
问题是您在测试用例中创建了不同的 React 上下文,而不是使用在 ./context/theme/ThemeProvider
模块中创建的 ThemeContext
。
也就是说useContext()
hook在App
组件中使用的context应该是ThemeContext
,这样App
组件才能接收context值并订阅值从 ThemeContext.Provider
.
例如
app.tsx
:
import React, { useContext, useState } from 'react';
import { ThemeContext } from './theme-provider';
export const App = (): JSX.Element => {
const { changeTheme, activeTheme } = useContext(ThemeContext);
const [theme, setTheme] = useState<string>(activeTheme);
const setNewTheme = (newTheme: string) => {
changeTheme(newTheme);
setTheme(newTheme);
};
return (
<div>
<h1>Project Name</h1>
<button
data-testid="app-theme-btn1"
onClick={() => {
setNewTheme('dark');
}}
>
Click here
</button>
<p>{`Active Theme: ${theme}`}</p>
</div>
);
};
theme-provider.tsx
:
import React, { useState } from 'react';
const defaultContext = {
activeTheme: 'light',
changeTheme: (newTheme: string) => {},
};
export const ThemeContext = React.createContext(defaultContext);
export const ThemeProvider = ({ children }) => {
const [activeTheme, setActiveTheme] = useState(defaultContext.activeTheme);
const changeTheme = (newTheme: string) => {
setActiveTheme(newTheme);
};
return <ThemeContext.Provider value={{ activeTheme, changeTheme }}>{children}</ThemeContext.Provider>;
};
app.test.tsx
:
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { App } from './app';
import { ThemeContext } from './theme-provider';
describe('71901699', () => {
test('should pass', async () => {
const defaultTheme = 'light';
const themeContext = {
activeTheme: defaultTheme,
changeTheme: jest.fn().mockImplementation((newTheme) => {
themeContext.activeTheme = newTheme;
}),
};
render(
<ThemeContext.Provider value={themeContext}>
<App />
</ThemeContext.Provider>
);
const themeButton = screen.getByTestId('app-theme-btn1');
userEvent.click(themeButton);
expect(themeContext.changeTheme).toHaveBeenCalledTimes(1);
await waitFor(() => screen.queryByText('Active Theme: dark'));
});
});
测试结果:
PASS Whosebug/71901699/app.test.tsx (8.691 s)
71901699
✓ should pass (74 ms)
--------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------|---------|----------|---------|---------|-------------------
All files | 80 | 100 | 50 | 77.78 |
app.tsx | 100 | 100 | 100 | 100 |
theme-provider.tsx | 55.56 | 100 | 0 | 50 | 10-14
--------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.217 s