使用Jest& amp;异步提取后测试组件的状态酶(使用模块)

时间:2018-01-07 00:00:18

标签: reactjs asynchronous mocking enzyme jest

我正在尝试使用Jest测试React组件的状态。 在ComponentDidUpdate上,如果有人登录并使用用户名更新state.user,它将在服务器上获取用户名。 如果没有用户登录,则会将state.pleaseLogin切换为true。

import React from "react";
import "./Header.css";
import "whatwg-fetch";
import LoggedInNav from "./LoggedInNav";
import LoggedOutNav from "./LoggedOutNav";
import urls from "../../urls";
import getUser from "../fetch/getUser";

export default class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      user: null,
      pleaseLogin: false
    };
    this.handleGetUser = this.handleGetUser.bind(this);
  }

  handleGetUser() {
    getUser().then(res => {
      if (res.hasOwnProperty("username")) {
        this.setState({
          user: res.username
        });
      } else {
        this.setState({
          pleaseLogin: true
        });
      }
    });
  }
  componentDidMount() {
    this.handleGetUser();
  }

  render() {
    let nav;
    if (this.state.user) {
      nav = <LoggedInNav user={this.state.user} />;
    } else if (this.state.pleaseLogin) {
      nav = <LoggedOutNav />;
    } else {
      nav = null;
    }
    return (
      <header className="app-header">
        <h1 className="header-brand">Watchers</h1>
        {nav}
      </header>
    );
  }
}

以下是getUser()的代码,用于在服务器上获取用户名:

export default () => {
  return fetch(`${BASE_URL}/api/user/user_data`, {
    credentials: "include"
  })
    .then(res => res.json())
    .catch(error => {
      console.log(error);
      return error;
    });
};

我手动模拟了API调用。该文件名为“getUser.js”,位于 mocks 文件夹中,紧挨着原始的“getUser.js”,如下所示:

export default () => {
  return Promise.resolve({
    username: 'Username'
  })
}

然后测试:

import React, { Component } from "react";
import Enzyme, { shallow, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import Header from "../react/components/Header";
import getUser from "../react/fetch/getUser";

Enzyme.configure({ adapter: new Adapter() });

jest.mock("../react/fetch/getUser");
const wrapper = mount(<Header />);

describe("<Header/>", () => {
  describe("component class", () => {
    it("should update state.user after componentDidMount()", () => {
      return getUser().then(() => {
        expect(wrapper.state()).toEqual({
          user: "Username",
          pleaseLogin: false
        });
      });
    });
  });
});

测试通过,状态发生了变化,state.user现在等于“Username”。 如果我更改我的模拟模块并使其返回一个空对象,它也可以正常工作,并且state.pleaseLogin切换为true。但是我必须手动测试这两种情况。 我尝试使用jest.mock('... module',()=&gt; {})直接在我的测试文件中模拟这两个不同的实现,并使用jest.doMock('... module',()= &gt; {})但是当我运行测试时状态不会更新;虽然它与手动模拟。我不确定问题的来源,也许这与异步性或在Jest中嘲笑有关?

1 个答案:

答案 0 :(得分:1)

我找到了两种不同的测试方法。

由于模拟模块仅包含一个函数,因此手动模拟可能不是最佳解决方案。不过这是我写测试的方式:

1。 __模拟__ 文件夹中的手动模拟

// In the example, the function getUser is exported with 'export default'.
module.exports = jest.fn(() => Promise.resolve({}))


// If the function is exported with 'export const getUser = () => {}':
module.exports = {
    getUser: jest.fn(() => Promise.resolve({}))
}

<强>测试

jest.mock("../react/fetch/getUser");
const getUser = require("../react/fetch/getUser");

// If function exported with 'export const getUser = () => {}':
// const {getUser} = require("../react/fetch/getUser");

describe("<Header/>", () => {
  it("should update state.user after componentDidMount()", () => {

    getUser.mockImplementation(() =>
      Promise.resolve({ username: "Username" })
    );

    const wrapper = shallow(<Header />);

    expect.assertions(1);

    return (
      getUser()
        .then(() => {
          expect(wrapper.state()).toEqual({
            user: "Username",
            pleaseLogin: false
          });
        })
    );
  });

  it("should update state.pleaseLogin after componentDidMount()", () => {
    getUser.mockImplementation(() => Promise.resolve({}));
    const wrapper = mount(<Header />);
    return (
      getUser()
        .then(() => {
          expect(wrapper.state()).toEqual({
            user: null,
            pleaseLogin: true
          });
        })
    );
  });
});

2。创建模拟函数以在测试代码中使用

测试

// If function exported with 'export default'
jest.mock("../react/fetch/getUser", () => jest.fn());
const getUser = require("../react/fetch/getUser");

// Else
// jest.mock("../react/fetch/getUser", () => ({getUser: jest.fn()}));
// const {getUser} = require("../react/fetch/getUser")

describe("<Header/>", () => {
  it("should update state.user after componentDidMount()", () => {
    const promise = Promise.resolve({
      username: "Username"
    });
    getUser.mockImplementation(() => promise);

    const wrapper = shallow(<Header />);

    expect.assertions(1);
    return getUser().then(() => {
      expect(wrapper.state()).toEqual({
        user: "Username",
        pleaseLogin: false
      });
    });
  });
});

值得注意的一些事情:

  • 模拟内部测试

我试图在测试中使用mock和doMock失败。它可能与此问题有关:https://github.com/facebook/jest/issues/2582

  • MockImplementation
  

当您需要定义时,mockImplementation方法很有用   从另一个创建的模拟函数的默认实现   模块

然而,mockImplementationOnce()将在此测试中抛出错误

  • Enzyme shallow()/ mount()

必须在模拟之后调用Enzyme shallow()或mount()才能使用该实现。否则我们得到&#39; TypeError:无法读取属性&#39;然后&#39;未定义&#39;

  • 返回承诺

需要退回承诺。如果没有&#39;返回&#39;,期望&#39;那么&#39;将被跳过,因为我们不等待异步调用返回。

  

Jest Docs:&#34;请务必退还承诺 - 如果省略此退货   声明,您的测试将在fetchData完成之前完成。&#34;

您可以查看以下链接,了解如果省略return,会发生什么:https://github.com/facebook/jest/issues/1924