您如何调试浅层提纯酶测试? [英] How do you debug a shallow rendered enzyme test?

查看:49
本文介绍了您如何调试浅层提纯酶测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的react-redux应用程序中修复失败的测试.当我再次潜水并再次进入组件时,我期望在其中看到JSX.但是,我什么也没看到.

这是我的组成部分-

  const Dashboard =(props)=>{如果(props.isSignedIn)返回 (< div className ="dashboard">< h1>欢迎</h1></div>);返回null;};const mapStateToProps =(状态)=>{返回{isSignedIn:state.auth.isSignedIn};};导出默认的connect(mapStateToProps,{signIn,signOut})(仪表板); 

这是我的考试不及格:-

  const setup =(initialState = {})=>{const store = storeFactory(initialState);const包装=浅(<仪表板商店= {商店}/>).dive().dive();返回包装;};describe("on render",()=> {describe(用户已登录",()=> {让包装纸;beforeEach(()=> {const initialState = {isSignedIn:true};包装=设置(initialState);});it(渲染仪表板",()=> {const component = wrapper.find("dashboard");Expect(component.length).toBe(1);});}); 

我的商店工厂:-

  export const storeFactory =(initialState)=>{const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);console.log(initialState);返回createStoreWithMiddleware(rootReducer,initialState);}; 

我的测试错误:-

 ●用户已登录›呈现仪表板Expect(received).toBe(expected)//Object.is相等预期:1收到:0 

当我潜水一次时,它看起来像这样:-

 <仪表板存储= {{......}} isSignedIn = {{...}} signIn = {[Function]} signOut = {[Function]}/>} 

但是当我尝试在仪表板组件中看到JSX时,我什么都看不到?

解决方案

我很确定您的设置无效,因为您正尝试 shallow 挂载一个redux连接的组件-该组件是一种高阶成分(HOC),包裹了由于酶的浅浅限制而无法正确地潜入其中的另一种成分.

相反,您有两个选择:

选项1.)推荐:导出 Dashboard 组件并使用 shallow mount 对其进行断言(我通常在 shallow 上使用 mount ,专门是为了避免过多和重复的 .dive()调用.)

首先导出未连接的组件:

  export const Dashboard =(props)=>{如果(props.isSignedIn)返回 (< div className ="dashboard">< h1>欢迎</h1></div>);返回null;}导出默认的connect(...)(仪表板) 

然后在您的测试中,导入仪表板组件(不是是默认的已连接导出):

 从酶"导入{mount};从"./Dashboard"导入{Dashboard};//导入命名的导出仪表板"const initialProps = {isSignedIn:否}const wrapper = mount(< Dashboard {... initialProps}/>);//或者,您可以在此处使用浅//现在使用wrapper.setProps(...)操作包装器 

选项2.)不推荐:使用真实的 store mount Provider 中>连接的HOC:

 从酶"导入{mount};从"redux"导入{提供者};从"redux"导入{createStore,applyMiddleware};从"redux-thunk"导入thunk;从"../path/to/reducers"导入rootReducer;从"../path/to/types"导入类型;从"./Dashboard"导入仪表板;//导入连接的默认导出const store = createStore(rootReducer,undefined,applyMiddleware(thunk));const initialProps = {isSignedIn:否};const wrapper = mount(< Provider store = {store}><仪表板{... initialProps}/></Provider>);//现在,您将按类型分派实际操作,并期望redux存储更改仪表板组件 

有关更多测试信息,请查看以下


可重复使用的HOC包装器:

utils/HOCWrapper/index.js

  import React,{createElement} from"react";从"redux"导入{createStore,applyMiddleware};从"react-redux"导入{提供者};从"react-router-dom"导入{MemoryRouter};从酶"导入{mount};从"redux-thunk"导入thunk;从'./path/to/reducers'导入rootReducer;const middlewares = applyMiddleware([thunk]);导出const store = createStore(rootReducer,null,中间件);/***工厂功能为组件创建已安装的MemoryRouter + Redux包装器* @功能HOCWrapper* @param {node}组件-要安装的组件* @param {object} initialProps-特定于此设置的组件props.* @param {object}状态-组件的初始安装状态.* @param {array} initialEntries-MemoryRouter的初始路由条目.* @param {object}选项-酶的装载"的可选选项* @function createElement-在传入的组件周围创建一个包装器(现在我们可以在根目录上使用wrapper.setProps)* @returns {MountedRouterWrapper}*/导出常量HOCWrapper =(成分,initialProps = {},状态= null,initialEntries = ["/"],选项= {},)=>{const wrapper = mount(createElement(道具=>(< Provider store = {store}>< MemoryRouter initialEntries = {initialEntries}><组件{... props}/></MemoryRouter></Provider>),initialProps,),选项,);if(状态)wrapper.find(Component).setState(state);返回包装;};导出默认的HOCWrapper; 

要使用它,请导入HOCWrapper函数:

  import来自"./Example"的组件;从"./path/to/utils/HOCWrapper"导入HOCWrapper;//如果您需要访问商店,那么...//导入HOCWrapper,{存储}来自"./path/to/utils/HOCWrapper";const initialProps = {...};const initialState = {...};//可选(默认为null)const initialEntries = [...];//可选(默认为"/")const options = {...};//可选(默认为空对象)//使用上面提供的JSDoc进行参数细分const wrapper = HOCWrapper(Component,initialProps,initialState,initialEntries,options);//并非所有参数都是必需的,只是组件" 

I am trying to fix a failing test in my react-redux app. When I dive and dive again into my component, I expect to see the JSX within it. However, I don't see anything.

Here is my component -

      const Dashboard = (props) => {
          if (props.isSignedIn)
            return (
              <div className="dashboard">
                  <h1>Welcome</h1>
              </div>
            );
          return null;
        };

    const mapStateToProps = (state) => {
      return { isSignedIn: state.auth.isSignedIn };
    };
export default connect(mapStateToProps, { signIn, signOut })(Dashboard);

Here is my failing test :-

const setup = (initialState = {}) => {
  const store = storeFactory(initialState);
  const wrapper = shallow(<Dashboard store={store} />).dive().dive();
  return wrapper;
};

describe("on render", () => {
describe("the user is signed in", () => {
  let wrapper;
  beforeEach(() => {
    const initialState = { isSignedIn: true };
    wrapper = setup(initialState);
  });
  it("renders the dashboard", () => {
    const component = wrapper.find("dashboard");
    expect(component.length).toBe(1);
  });
});

My store factory :-

export const storeFactory = (initialState) => {
  const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);
  console.log(initialState);
  return createStoreWithMiddleware(rootReducer, initialState);
};

My test error :-

● the user is signed in › renders the dashboard

    expect(received).toBe(expected) // Object.is equality

    Expected: 1
    Received: 0

When I dive one time it looks like this :-

 <Dashboard store={{...}} isSignedIn={{...}} signIn={[Function]} signOut={[Function]} />}

but when I try to see the JSX inside of the dashboard component I see nothing?

解决方案

I'm pretty sure your setup isn't working because you're trying to shallow mount a redux connected component -- which is a higher-order component (HOC) wrapping another component that can't be properly dived into due to enzyme's shallow limitations.

Instead, you have two options:

Option 1.) Recommended: Export the Dashboard component and make assertions against it using shallow or mount (I mostly use mount over shallow specifically to avoid excessive and repetitive .dive() calls).

First export the unconnected component:

export const Dashboard = (props) => {
  if (props.isSignedIn)
    return (
      <div className="dashboard">
        <h1>Welcome</h1>
      </div>
    );

  return null;
}

export default connect(...)(Dashboard)

Then in your test, import the Dashboard component (not the default connected export):

import { mount } from "enzyme";
import { Dashboard } from "./Dashboard"; // importing named export "Dashboard"

const initialProps = {
  isSignedIn: false
}

const wrapper = mount(<Dashboard { ...initialProps } />); // alternatively, you can use shallow here

// now manipulate the wrapper using wrapper.setProps(...);

or

Option 2.) Not recommended: Wrap the component in a real Provider with a real store and mount the connected HOC:

import { mount } from "enzyme";
import { Provider } from "redux";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../path/to/reducers";
import types from "../path/to/types";
import Dashboard from "./Dashboard"; // importing connected default export

const store = createStore(rootReducer, undefined, applyMiddleware(thunk));

const initialProps = {
  isSignedIn: false
};

const wrapper = mount(
  <Provider store={store}>
    <Dashboard { ...initialProps } />
  </Provider>
);

// now you'll dispatch real actions by type and expect the redux store to change the Dashboard component

For more testing information, please take a look at this answer, which covers a similar example (skip to the bullet points; ignore the MemoryRouter pieces; and, while the example is a bit old, the testing pattern is the same).

The reason I'd recommend option 1 over option 2 is that it's much easier to manage, as you're directly manipulating the component you're testing (not a HOC wrapping your component). Option 2 is only useful if you absolutely need to test the entire workflow of redux to the connected component. I find option 2 to be mostly overkill as you can unit test each piece (actions, reducers, and unconnected components) individually and achieve the same testing coverage. In addition, as I mentioned in this example, I find redux-mock-store to be mostly useful for unit testing redux actions.

On a side note, you can see what enzyme sees by using console.log(wrapper.debug()); in a test!


Example unconnected test:

import React, { createElement } from "react";
import { configure } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import { mount } from "enzyme";
import { MemoryRouter } from "react-router-dom";
import { Dashboard } from "./App.js";

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

const initialProps = {
  isSignedIn: false
};

describe("Unconnected Dashboard Component", () => {
  let wrapper;
  beforeEach(() => {
    /* 
      This code below may be a bit confusing, but it allows us to use
      "wrapper.setProps()" on the root by creating a function that first
       creates a new React element with the "initialProps" and then
       accepts additional incoming props as "props". 
    */
    wrapper = mount(
      createElement(
        props => (
          <MemoryRouter initialEntries={["/"]}> // use memory router for testing (as recommended by react-router-dom docs: https://reacttraining.com/react-router/web/guides/testing)
            <Dashboard {...props} />
          </MemoryRouter>
        ),
        initialProps
      )
    );
  });

  it("initially displays nothing when a user is not signed in", () => {
    expect(wrapper.find(".dashboard").exists()).toBeFalsy();
  });

  it("displays the dashboard when a user is signed in", () => {
    wrapper.setProps({ isSignedIn: true });
    expect(wrapper.find(".dashboard").exists()).toBeTruthy();
  });
});

Working example (click on the Tests tab to run tests):


Reuseable HOC wrapper:

utils/HOCWrapper/index.js

import React, { createElement } from "react";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import { mount } from "enzyme";
import thunk from "redux-thunk";
import rootReducer from './path/to/reducers';

const middlewares = applyMiddleware([thunk]);
export const store = createStore(rootReducer, null, middlewares);

/**
 * Factory function to create a mounted MemoryRouter + Redux Wrapper for a component
 * @function HOCWrapper
 * @param {node} Component - Component to be mounted
 * @param {object} initialProps - Component props specific to this setup.
 * @param {object} state - Component initial state for setup.
 * @param {array} initialEntries - Initial route entries for MemoryRouter.
 * @param {object} options - Optional options for enzyme's "mount"
 * @function createElement - Creates a wrapper around passed in component (now we can use wrapper.setProps on root)
 * @returns {MountedRouterWrapper}
 */
export const HOCWrapper = (
    Component,
    initialProps = {},
    state = null,
    initialEntries = ["/"],
    options = {},
) => {
    const wrapper = mount(
        createElement(
            props => (
                <Provider store={store}>
                    <MemoryRouter initialEntries={initialEntries}>
                        <Component {...props} />
                    </MemoryRouter>
                </Provider>
            ),
            initialProps,
        ),
        options,
    );
    if (state) wrapper.find(Component).setState(state);
    return wrapper;
};

export default HOCWrapper;

To use it, import the HOCWrapper function:

import Component from "./Example";
import HOCWrapper from "./path/to/utils/HOCWrapper"; 
// if you need access to store, then... 
// import HOCWrapper, { store } from "./path/to/utils/HOCWrapper";

const initialProps = { ... };
const initialState = { ... }; // optional (default null)
const initialEntries = [ ... ]; // optional (default "/")
const options = { ... }; // optional (default empty object)

// use the JSDoc provided above for argument breakdowns
const wrapper = HOCWrapper(Component, initialProps, initialState, initialEntries, options); // not all args are required, just the "Component"

这篇关于您如何调试浅层提纯酶测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆