Модульный тест компонента, заключенного в response-intl, выдает `TypeError: _react.default.useContext ... возвращаемое значение не является итерируемым` - PullRequest
0 голосов
/ 20 июня 2020

Я знаю, что мне нужно передать контекст тестируемому компоненту, и я пробовал несколько разных способов, но, похоже, не могу заставить его работать. При такой настройке я получаю следующую ошибку:

    TypeError: _react.default.useContext is not a function or its return value is not iterable

       7 |
       8 | function MyComponent(props) {
    >  9 |   const [locale, setLocale] = React.useContext(LocaleContext);
         |                                     ^

Есть какие-нибудь советы о том, что я делаю неправильно? Мне лучше перейти на фермент или react-test-renderer?

App. js

import React from "react";
import { IntlProvider } from "react-intl";

import MyComponent from "./components/MyComponent ";

import "./App.css";

import { LocaleContext } from "./LocaleContext";
const messages = { zh: require("./translations/zh") };

function App() {
  const [locale] = React.useContext(LocaleContext);

  return (
    <IntlProvider locale={locale} messages={messages[locale]}>
      <div className="App">
        <MyComponent />
      </div>
    </IntlProvider>
  );
}

export default App;

LocaleContext. js

import React, { useContext } from "react";
export const LocaleContext = React.createContext();
export const useLocaleContext = () => useContext(LocaleContext);

export const LocaleContextProvider = props => {
  const [locale, setLocale] = React.useState("en");
  return (
    <LocaleContext.Provider value={[locale, setLocale]}>
      {props.children}
    </LocaleContext.Provider>
  );
};

MyComponent. js

import React from "react";
import { FormattedMessage } from "react-intl";

import { LocaleContext } from "../LocaleContext";

import logo from "../logo.svg";

function MyComponent(props) {
  const [locale, setLocale] = React.useContext(LocaleContext);
  const nextLocale = locale === "en" ? "zh" : "en";

  return (
    <header className="App-header">
      <img src={logo} className="App-logo" alt="logo" />
      <h1>
        <FormattedMessage id="title" defaultMessage="Hello World!" />
      </h1>
      <h2>
        <FormattedMessage id="subtitle" defaultMessage="Welcome to our app" />
      </h2>
      <button onClick={() => setLocale(nextLocale)}>
        Change language to {nextLocale}
      </button>
    </header>
  );
}

export default MyComponent;

MyComponent.test. js

import React from "react";
import { render } from "@testing-library/react";

import * as LocaleContext from "../LocaleContext";
import MyComponentfrom "./MyComponent";

test("renders `hello world` heading", () => {
  const contextValues = { title: "Hey There" };
  jest
    .spyOn(LocaleContext, "useLocaleContext")
    .mockImplementation(() => contextValues);

  const { getByText } = render(<MyComponent/>);
  const helloWorldText = getByText(/hello world/i);
  expect(helloWorldText).toBeInTheDocument();
});

пакет. json

{
  "scripts": {
    ...
    "test": "react-scripts test"
  },
  "dependencies": {
    "@testing-library/jest-dom": "^4.2.4",
    "@testing-library/react": "^9.3.2",
    "react": "^16.13.1",
    "react-intl": "^4.6.9",
    "react-scripts": "3.4.1"
    ...
  }
  ...
}

Ответы [ 2 ]

2 голосов
/ 22 июня 2020

drew-reese получает ответ за ответ, но я хотел повторить код, который использовал, поскольку он не использовал LocaleContext в оболочке.

Это также включало установку дополнительной зависимости для компиляции с ICU.

npm i --save-dev full-icu

LocaleContext. js

import React, { useContext } from "react";
import { IntlProvider } from "react-intl";

const messages = { zh: require("./translations/zh") };

export const LocaleContext = React.createContext();
export const useLocaleContext = () => useContext(LocaleContext);

export const LocaleContextProvider = props => {
  const [locale, setLocale] = React.useState("en");
  return (
    <LocaleContext.Provider value={[locale, setLocale]}>
      {props.children}
    </LocaleContext.Provider>
  );
};

export const intlEnWrapper = {
  wrapper: ({ children }) => <IntlProvider locale="en" messages={messages.en}>{children}</IntlProvider>
};
export const intlZhWrapper = {
  wrapper: ({ children }) => <IntlProvider locale="zh" messages={messages.zh}>{children}</IntlProvider>
};

MyComponent.test. js

import React from "react";
import { render } from "@testing-library/react";

import { intlEnWrapper, intlZhWrapper } from "../../LocaleContext";
import MyComponentfrom "./index";

describe("For en locale", () => {
  test("renders `Title Text` heading", () => {
    const { getByText } = render(<MyComponent/>, intlEnWrapper);
    const titleText = getByText(/title text/i);
    expect(titleText).toBeInTheDocument();
  });
});

describe("For zh locale", () => {
  test("renders `Title Text` heading", () => {
    const { getByText } = render(<MyComponent />, intlZhWrapper);
    const titleText = getByText(/標題文字/i);
    expect(titleText).toBeInTheDocument();
  });
});

пакет. json

{
  "scripts": {
    ...
    "test": "cross-env NODE_ICU_DATA=node_modules/full-icu react-scripts test"
  }
}
    
2 голосов
/ 20 июня 2020

Возможно, вы захотите изучить API-интерфейс оболочки и настроить пользовательский рендеринг , но суть в том, что вы создаете тестовую оболочку, которая предоставляет поставщик контекста для тестирования.

Например, я использую response-intl, поэтому для тестирования у меня есть тестовая утилита intlWrapper

import React from 'react';
import { IntlProvider } from 'react-intl';

export const intlWrapper = ({ children }) => (
  <IntlProvider locale="en">{children}</IntlProvider>
);

А для тестирования компонента он используется как таковой

const {/* query selectors */} = render(
  <ComponentUsingIntl />,
  { wrapper: intlWrapper },
);

В соответствии с вашими потребностями, я думаю, вам следует создать оболочку для LocaleContextProvider

import { LocaleContextProvider } from '../LocaleContext';

export const contextWrapper = ({ children }) => (
  <LocaleContextProvider>{children}</LocaleContextProvider>
);

Теперь вы можете импортировать свой тестовый contextWrapper и использовать

const { getByText } = render(<MyComponent/>, { wrapper: contextWrapper });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...