Тестирование / макетирование сервиса внутри функционального контейнера в React / Jest - PullRequest
0 голосов
/ 06 марта 2020

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

const gameService = new Game();

const Toolbar = () => {
  const [name, setName] = useState("");
  const [addVisible, setAddVisible] = useState(false);
  const [loading, setLoading] = useState(false);
  const errorContext = useContext(ErrorContext);
  const appContext = useContext(AppContext);

  const addGame = async () => {
    setLoading(true);
    try {
      const updatedGames = await gameService.add({
        name
      });
      appContext.setGames(updatedGames);
      setLoading(false);
      setName('');
      setAddVisible(false);
      errorContext.setError(undefined);
    } catch (e) {
      errorContext.setError(e);
      setLoading(false);
    }
  };

  return (
    <StyledToolbar>
      Games
      <IconButton
        style={{ float: "right" }}
        onClick={() => setAddVisible(true)}
      >
        <Plus size={12} />
      </IconButton>
      <br />
      {addVisible && (
        <div className="form">
          <form onSubmit={() => addGame()}>
            <label htmlFor="name">Board Name</label>
            <input
              disabled={loading}
              id="name"
              name="name"
              placeholder="My Board 1"
              value={name}
              onChange={e => setName(e.target.value)}
            />
            <div style={{ float: "right" }}>
              <IconButton type="submit" disabled={loading}>
                <ArrowRight size={15} />
              </IconButton>
              <IconButton
                disabled={loading}
                onClick={() => {
                  setAddVisible(false);
                  setName("");
                }}
              >
                <X size={15} />
              </IconButton>
            </div>
          </form>
        </div>
      )}
    </StyledToolbar>
  );
};

Это игровой сервис, который обрабатывает вызовы API

class Game {
  constructor() {
    this.instance = axios.create({
      baseURL: API_URL,
      timeout: TIMEOUT
    });
  }

  getAll() {
    return new Promise(async (resolve, reject) => {
      try {
        const game = await this.instance.get('/games');
        resolve(game.data);
      } catch (e) {
        reject(e.message);
      }
    });
  }

  getById(id) {
    return new Promise(async (resolve, reject) => {
      try {
        const game = await this.instance.get(`/games/${id}`);
        resolve(game.data);
      } catch (e) {
        reject(e.message);
      }
    });
  }

  update(id, body) {
    return new Promise(async (resolve, reject) => {
      try {
        const game = await this.instance.put(`/games/${id}`, body);
        resolve(game.data);
      } catch (e) {
        reject(e.message);
      }
    });
  }

  add(body) {
    return new Promise(async (resolve, reject) => {
      try {
        const game = await this.instance.post('/games', body);
        resolve(game.data);
      } catch (e) {
        reject(e.message);
      }
    });
  }

  delete(id) {
    return new Promise(async (resolve, reject) => {
      try {
        const game = await this.instance.delete(`/games/${id}`);
        resolve(game.data);
      } catch (e) {
        reject(e.message);
      }
    });
  }
}

, и это пока что тест, который я написал, и я не могу понять, как Я могу проверить метод «Добавить» этого класса. Я пытаюсь смоделировать щелчок и пытаюсь смоделировать / шпионить над методом добавления.

describe("Toolbar Add functionality", () => {
  let wrapper;

  beforeEach(() => {
    wrapper = mount(<Toolbar />);
    wrapper.find("IconButton").simulate("click");
  });

  it("Should make an API call when clicked add button", () => {
    jest.mock("../services/game");
    const gameService = new Game();
    console.log(gameService);
    const getSpy = jest.spyOn(gameService, "add");

    const inputField = wrapper.find("form").find("input");
    inputField.simulate("change", { target: { value: "name" } });
    wrapper
      .find("form")
      .find("IconButton[type='submit']")
      .simulate("click");
    expect(getSpy).toBeCalled();
  });
});

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

1 Ответ

0 голосов
/ 07 марта 2020

Вам нужно подождать, пока обещание разрешит, установив свой тест как async и используя await / act, я использую этот метод, но вы можете найти другие варианты, набрав в Google jest asynchronous testing

  import { act } from 'react-dom/test-utils';
  ...
  describe("Toolbar Add functionality", async () => {
    let wrapper;
    let gameService;

    beforeEach(() => {
      // I'm not sure about this arragement for gameService creation though
      jest.mock("../services/game");
      const gameService = new Game();
      wrapper = mount(<Toolbar gameService={gameService} />);
      wrapper.find("IconButton").simulate("click");
    });

    it("Should make an API call when clicked add button", () => {
      console.log(gameService);
      const getSpy = jest.spyOn(gameService, "add");

      const inputField = wrapper.find("form").find("input");
      inputField.simulate("change", { target: { value: "name" } });
      wrapper
        .find("form")
        .find("IconButton[type='submit']")
        .simulate("click");
      await act(async () => await Promise.resolve());
      expect(getSpy).toBeCalled();
    });
  });

ОБНОВЛЕНИЕ

Поскольку ваш игровой сервис является классом, и вы создаете новые объекты для его использования, необходимо будет передать его в качестве реквизита, чтобы вы могли чтобы шпионить за реальным объектом, компонент использует


<strike>const gameService = new Game();</strike>

const Toolbar = ({gameService}) => {
  const [name, setName] = useState("");
  ...
}
...