在提供的上下文中实现的功能,而不是开玩笑模拟

发布于 2025-01-21 20:48:17 字数 2132 浏览 0 评论 0原文

我需要测试React应用程序中的单击按钮,该按钮调用上下文提供的函数。当调用上下文在嘲笑测试中提供的模拟函数时,调用了实际函数。如何解决这个问题?

应用程序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)
  })

I need to test the click on a button in a React application which invokes a function provided by the context. When invoking the mocked function provided by the context in a Jest test the actual function is called. What to do to resolve this ?

Application 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>
  )
}

Actual test

  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)
  })

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

划一舟意中人 2025-01-28 20:48:17

问题是您在测试案例中创建不同的反应上下文,而不是使用./ ./ context/themey/theaprovider 模块中创建的themecontext

换句话说,usecontext() app组件中使用的上下文应为themeContext,以便app组件可以接收上下文值并订阅从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  stackoverflow/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

The problem is you create a different React context in the test case rather than using the ThemeContext created in ./context/theme/ThemeProvider module.

In other words, the context used by useContext() hook in the App component should be the ThemeContext so that App component can receive the context value and subscribe to the value changes from ThemeContext.Provider.

E.g.

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'));
  });
});

Test result:

 PASS  stackoverflow/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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文