enzyme 简单介绍与使用

Enzyme 来自 airbnb 公司,是一个用于 React 的 JavaScript 测试工具,方便你判断、操纵和历遍 React Components 输出。

Enzyme 的 API 通过模仿 jQuery 的 API ,使得 DOM 操作和历遍很灵活、直观。Enzyme 兼容所有的主要测试运行器和判断库,

安装与配置

在umi中使用enzyme之前,需要在项目中安装enzyme依赖,安装的命令如下:

npm i --save-dev enzyme enzyme-adapter-react-16

npm i --save-dev @types/enzyme

最后,您需要配置酶以使用您要使用的适配器。为此,您可以使用顶级configure(...)API。

import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

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

enzyme支持三种方式的渲染:

shallow:浅渲染,是对官方的Shallow Renderer的封装。将组件渲染成虚拟DOM对象,只会渲染第一层,子组件将不会被渲染出来,因而效率非常高。不需要DOM环境, 并可以使用jQuery的方式访问组件的信息;

render:静态渲染,它将React组件渲染成静态的HTML字符串,然后使用Cheerio这个库解析这段字符串,并返回一个Cheerio的实例对象,可以用来分析组件的html结构。

mount:完全渲染,它将组件渲染加载成一个真实的DOM节点,用来测试DOM API的交互和组件的生命周期,用到了jsdom来模拟浏览器环境。

简单使用介绍

为了方便讲解Enzyme测试的用法,我们首先新建一个enzyme.jsx的测试文件。代码如下:

import React from 'react'

const Example = (props)=>{
    return (
        <div>
            <div id="title">{props.title}</div>
            <button>{props.text}</button>
        </div>
    )
}
export default Example

浅渲染shallow

Shallow Rendering用于将一个组件渲染成虚拟DOM对象,但是只渲染第一层,不渲染所有子组件,所以处理速度非常快。并且它不需要DOM环境,因为根本没有加载进DOM。

为了进行浅渲染shallow测试,我们新建一个名为enzyme.test.js的测试文件。

import React from 'react'
import Enzyme from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import Example from '../src/components/enzyme'

const { shallow } = Enzyme
Enzyme.configure({ adapter: new Adapter() })

describe('Example 组件相关',  ()=> {
    it('测试组件传值 文本是否符合预期',  ()=> {
        const button = '按钮名';
        const title = '标题';

        let app = shallow(<Example text={button} title={title}  />);
        
        const appButton = app.find('button').text();
        const appTitle =  app.find('#title').text();

        // 判断名称是否跟标签文本名称一致
        expect(button).toBe(appButton);
        expect(title).toBe(appTitle);
    })
})

执行测试案例可以看到正常通过

Enzyme工具api

.childAt(index) => ShallowWrapper 返回具有指定索引的子元素到新的wrapper
.find(selector) => ShallowWrapper 根据选择器查找节点,找到渲染树中的节点。selector可以是CSS中的选择器,也可以是组件的构造函数,以及组件的display name等;

例:

   const loginComponent = outLayer.find('form')
   const usernameInput = outLayer.find('input#userName')
   const passwordInput = outLayer.find('input[type="password"]')
   const submitButton = outLayer.find('button[type="submit"]')
   const errorComponent = outLayer.find('div.ant-alert-error')
describe('测试 counter2Component 文件', function () {
  // 浅渲染 Counter2
  const wrapper = shallow(<Counter2 />)
  
  // 查找 a 便签的属性 href 是否为 #
  it('a 标签的属性 href 为 #', function () {
    expect(
      wrapper.find('a').prop('href')
    ).to.equal('#')
  })
  // 通过属性 [type="text"] 查找
  it('[type="text"] is existing',() => {
    expect(
      wrapper.find('[type="text"]').length
    ).to.equal(1)
  })
   // 测试是否含有两个 button 标签
  it('Counter2 includes two button elements',() => {
    expect(
      wrapper.find('button')
    ).to.have.lengthOf(2)
  })
  // 通过 className 查找标签
  it('it includes an element which className is addBtn',() => {
    expect(
      wrapper.find('.addBtn')
    ).to.have.lengthOf(1)
  })
.findWhere(predicate) => ShallowWrapper 找到渲染树中里被的断言函数返回true的节点 参数:predicate (ShallowWrapper => Boolean) 断言函数返回布尔值
.filter(selector) => ShallowWrapper 过滤当前包装器中与所提供的选择器不匹配的节点。
.filterWhere(predicate) => ShallowWrapper 过滤当前包装器里被断言函数predicate不返回true的节点
.contains(nodeOrNodes) => Boolean 返回给定的 节点/节点数组 是否在渲染树中的布尔值。当前对象是否包含参数重点 node,参数类型为react对象或对象数组;
/* Counter2component.js */
class Counter2 extends React.Component{
    render(){
    const num = 4;
        return(
            <div>
                <div data-id="id" data-name="name" className="container">id</div>
            </div>
            <div data-num="num">
              {num}
            </div>
        )
    }
}

/* test/components.test.js */
describe('测试 counter2Component 文件', function () {
  const wrapper = shallow(<Counter2 />)
  it('测试是否包含完整自定义属性便签',() => {
    expect(
      wrapper.contains(
        <div data-id="id" data-name="name"className="container">id</div>) // 必须完整的书写,有一点不同,都无法通过
    ).to.equal(true)
  })
   const num = 4;
    expect(
      wrapper.contains(<div data-num="num">{num}</div>)
    ).to.equal(true);
})

contains()期望使用一个ReactElement,而不是选择器(与许多其他方法一样)。确保在调用它时,使用的是ReactElement或JSX表达
请记住,此方法还基于节点的子节点的相等来确定相等。

.containsMatchingElement(node) => Boolean 返回在浅渲染树中是否存在给定的node节点 的布尔值。
/* Counter2component.js */
class Counter2 extends React.Component{
    render(){
    const num = 4;
        return(
            <div>
                <div data-id="id" data-name="name" className="container">id</div>
            </div>
        )
    }
}

/* test/components.test.js */
describe('测试 counter2Component 文件', function () {
  const wrapper = shallow(<Counter2 />)
   it('测试是否匹配任意含有的属性标签', () => {
    expect(
      wrapper.containsMatchingElement(<div data-id="id">id</div>)
    ).to.equal(true);
    expect(
      wrapper.containsMatchingElement(<div data-id="id" data-num="num">id</div>)
    ).to.equal(false); // 匹配不到 data-num="num" 属性
})

.containsAllMatchingElements(nodes) => Boolean 返回在浅渲染树中是否存在给定的 所有 react元素 的布尔值。
/* Counter2component.js */
class Counter2 extends React.Component{
    render(){
        <div>
            <div data-id="id" data-name="name" className="container">
              id
            </div>
            <div data-num="num">
              {num}
            </div>
            <Counter className="foo" />
            <span className="foo"></span>
        </div>
    }
}

/* test/components.test.js */
describe('测试 counter2Component 文件', function () {
  const wrapper = shallow(<Counter2 />)
    it('测试是否匹配任意含有的属性标签数组', () => {
    expect(
      wrapper.containsAllMatchingElements(
        [<div data-id="id">id</div>,
          <span></span>,            
          <Counter className="foo" />] // 必须是数组
        )
    ).to.equal(true);
  })
})

.containsAnyMatchingElements(nodes) => Boolean 返回在浅渲染树中是否存在给定react元素 之一 的布尔值
 describe('测试 counter2Component 文件', function () {
  const wrapper = shallow((
    <div>
      <h1>hi</h1>
      <span>welcome</span>
    </div>
  ))
  it('匹配至少一个含有的标签数组', () => {
    expect(
      wrapper.containsAnyMatchingElements([<span>welcome</span>,<em>quite</em>])
    ).to.equal(true) // <span>welcome</span> 存在,<em>quite</em> 不存在
  })
})

.equals(node) => Boolean 根据期望值,返回当前渲染树是否等于给定节点的 布尔值
/* pComponent.js */
class PEle extends React.Component {
  render() {
    return <p><span>123</span></p>;
  }
}
/* test/components.test.js */
 describe('测试 pComponent 文件', function () {
  const wrapper = shallow(<PEle />)
  it('测试',() => {
    expect(
      wrapper.equals(<p><span>123</span>1</p>)
    ).to.equal(true); // 必须完全匹配
  })
})

.matchesElement(node) => Boolean 返回当前给定的react元素 是否 匹配浅渲染树 的布尔值
describe('测试 pComponent 文件', function () {
   const wrapper = shallow((
    <button type="button" 
        style={{border:"1px solid",backgroundColor:"blcak"}} 
        className="btn">
    hello</button>
  ))
  it('测试标签是否匹配', () => {
    expect(
      wrapper.matchesElement(<button 
          style={{border:"1px solid",backgroundColor:"blcak"}}  
          className="btn">hello
      </button>)
    ).to.equal(true);
  })
})

.hasClass(className) => Boolean 是否有这个className
.is(selector) => Boolean 当前节点是否与提供的选择器匹配
describe('测试 counter2Component 文件', function () {
  const wrapper = shallow(
    <div className="foo other" />
  )
  it('匹配 className',() => {
   expect(
     wrapper.is(".other")
   ).to.equal(true);
  }) 
});


.exists() => Boolean 当前节点是否存在
 const wrapper = shallow(
    <div className="foo other" />
  )
  it('测试某个 className 是否存在', () => {
    // 两种形式等价
    expect(
      wrapper.exists(".foo")
    ).to.equal(true);
    expect(
      wrapper.find(".foo").exists()
    ).to.equal(true)
});

.isEmptyRender( ) => Boolean 返回包装器最终是否只呈现允许的假值:false或null。
 function Foo() {
    return null;
  }
  const wrapper = shallow(<Foo />);
  it('测试 isEmptyRender 函数',() => {
    expect(
      wrapper.isEmptyRender()
    ).to.equal(true);
  })

.not(selector) => ShallowWrapper 删除当前wrapper中与所提供的选择器匹配的节点。 (与 .filter()作用相反)
 const wrapper = shallow(
    <div className="hello">
      <span className="foo">1</span>
      <span className="other">2</span>
      <span className="foo">1</span>
    </div>
  )
  it('测试 .not 函数',() => {
    expect(
      wrapper.find("span").not(".foo")
    ).to.have.lengthOf(1)
  })

.children() => ShallowWrapper 获取当前 wrapper 中所有子节点的 wrapper.
function Box() {
    return (
      <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
      </ul>
    );
}
const items = [1, 2, 3, 4];
  const wrapper = shallow(<Box items={items} />);
  it('测试 .children 函数', () => {
    expect(
      wrapper.find("ul").children()
    ).to.have.lengthOf(items.length)
});

.childAt(index) => ShallowWrapper 返回具有指定索引的子元素的 wrapper
function Box() {
     <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
        <li>1</li>
     </ul>
}
const wrapper = shallow(<Box />);
  it('测试 childAt 函数',() => {
    expect(
      wrapper.find('ul').childAt(0).type()
    ).to.equal('li')
})

.parents() => ShallowWrapper 获取当前节点的所有父级(祖先)
function Box() {
    return (
        <div>
            <ul>
              <li>1</li>
              <li>1</li>
              <li>1</li>
              <li>1</li>
            </ul>
        </div>
    )
}
const wrapper = shallow(<Box />);
  it('测试 childAt 函数',() => {
    expect(
      wrapper.find('ul').parents()
    ).to.lengthOf(1) // 查看父级的层数
})

.parent() => ShallowWrapper 获取当前节点的直接父级

.closest(selector) => ShallowWrapper 根据选择器,获取当前节点的第一个祖先

.shallow([options]) => ShallowWrapper Shallow renders the current node and returns a shallow wrapper around it.
function Bar() {
    return (
      <div>
        <div className="in-bar" />
      </div>
    );
  }
  function Foo() {
    return (
      <div>
        <Bar />
      </div>
    );
  }
  const wrapper = shallow(<Foo />);
  it('测试 shallow 函数', () => {
    expect(
      wrapper.find('.in-bar')
    ).to.have.lengthOf(0);
    expect(
      wrapper.find(Bar)
    ).to.have.lengthOf(1)
    expect(
      wrapper.find(Bar).shallow().find('.in-bar') // 浅渲染不能直接渲染 React 组件内部的属性,                                                     通过 shallow 属性才能访问 React 组件内                                                     部的属性
    ).to.have.lengthOf(1)
})

.render() => CheerioWrapper 返回当前节点的子树的CheerioWrapper

.unmount() => ShallowWrapper 卸载组件的方法

.text() => String 返回当前渲染树中文本节点的 字符串表示形式。当前组件的文本内容;
const wrapper = shallow(<div><b>important</b></div>);
expect(wrapper.text()).to.equal('important');
    
const wrapper = shallow(<div><Foo /><b>important</b></div>);
expect(wrapper.text()).to.equal('<Foo />important');

.html() => String 返回当前节点的静态HTML呈现
function Foo() {
  return (<div className="in-foo" />);
}
function Bar() {
  return (
    <div className="in-bar">
      <Foo />
    </div>
  );
}
const wrapper = shallow(<Bar />);
expect(wrapper.html()).to.equal('<div class="in-bar"><div class="in-foo"></div></div>');
expect(wrapper.find(Foo).html()).to.equal('<div class="in-foo"></div>');

.get(index) => ReactElement 返回给出索引的节点 ReactElement 要测试它,需要重新渲染;
 function Bar(props) {
    return(
      <p>{this.props.name}</p>
    )
  }
  function Foo() {
    return(
      <div>
        <Bar name="hello" />
      </div>
    )
  }
  const wrapper = shallow(<Foo />);
  it('测试 .get 函数',() => {
    expect(
      wrapper.find(Bar).get(0).props.name
    ).to.equal('hello')
  })

.getNode() => ReactElement 返回底层节点

.getNodes() => Array 返回底层的一些节点

.at(index) => ShallowWrapper 返回 参数:索引节点的 浅wrapper。
 function Bar(props) {
    return(
      <p>{this.props.name}</p>
    )
  }
  function Foo() {
    return(
      <div>
        <Bar name="hello" />
      </div>
    )
  }
  const wrapper = shallow(<Foo />);
  it('测试 .get 函数',() => {
    expect(
      wrapper.find(Bar).at(0).props().name
    ).to.equal('hello')
  })

.first() => ShallowWrapper 返回当前第一个节点 wrapper
  function Bar(props) {
    return(
      <div>
        <p>{this.props.name}</p>
        <span>{this.props.age}</span>
      </div>
    )
  }
  function Foo() {
    return(
      <div>
        <Bar name="hello" age="18" />
      </div>
    )
  }
  const wrapper = shallow(<Foo />);
  it('测试 .get 函数',() => {
    expect(
      wrapper.find(Bar).first().props().name
    ).to.equal('hello')
  })

.last() => ShallowWrapper 返回当前最后一个节点 wrapper
function Bar(props) {
    return(
      <div>
        <p>{this.props.name}</p>
        <span>{this.props.age}</span>
      </div>
    )
  }
  function Foo() {
    return(
      <div>
        <Bar name="hello" age="18" />
      </div>
    )
  }
  const wrapper = shallow(<Foo />);
  it('测试 .get 函数',() => {
    expect(
      wrapper.find(Bar).last().props().age
    ).to.equal('18')
  })

.state([key]) => Any 返回根组件的状态
class PEle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'lili',
    }
  }
  render() {
    return <p>
      <span></span>
      <span className="foo"></span>
      <span></span>
    </p>;
  }
}
 const wrapper = shallow(<PEle />);
  it('测试 .state 函数',() => {
    expect(wrapper.state('name')).to.equal('lili')
    expect(wrapper.state().name).to.equal('lili');
 })

.context([key]) => Any 返回根组件的上下文环境

.props() => Object 返回当前节点的 props
function MyComponent(props) {
    const { includedProp } = props;
    return (
      <div className="foo bar" includedProp={includedProp}>Hello</div>
    );
  }
  const wrapper = shallow(<MyComponent includedProp="Success!" excludedProp="I'm not    included" />);
it('测试 props 函数',() => {
    expect(wrapper.props().includedProp).to.equal('Success!');
})

.prop(key) => Any 返回当前节点props的某个(key)属性的值
 function MyComponent(props) {
    const { includedProp } = props;
    return (
      <div className="foo bar" includedProp={includedProp}>Hello</div>
    );
  }
  const wrapper = shallow(<MyComponent includedProp="Success!" excludedProp="I'm not    included" />);
it('测试 props 函数', () => {
    expect(wrapper.prop('includedProp')).to.equal('Success!');
})

.key() => String 返回当前节点的键(key)
const wrapper = shallow(
    <ul>
      {
        ['foo','bar'].map(item => <li key={item}>{item}</li>)
      }
    </ul>
  ).find('li')
  it('测试 .key 函数',() => {
    expect(
      wrapper.at(0).key()
    ).to.equal('foo')
    expect(
      wrapper.last().key()
    ).to.equal('bar');
  })

.simulate(event, mock) => 用来模拟事件触发,event为事件名称,mock为一个event object
 class Foo extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }

    render() {
      const { count } = this.state;
      return (
        <div>
          <div className={`clicks-${count}`}>
            {count} clicks
          </div>
          <a href="url" onClick={() => { this.setState({ count: count + 1 }); }}>
            Increment
          </a>
        </div>
      );
    }
  }
  const wrapper = shallow(<Foo />);
  it('测试 .simulate 函数', () => {
    wrapper.find('a').simulate('click');
    expect(wrapper.state('count')).to.equal(1)
    wrapper.find('a').simulate('click');
    expect(wrapper.state('count')).to.equal(2)
  })

.setState(nextState) => ShallowWrapper 手动setState更新根组件状态
 class Foo extends React.Component {
    constructor(props) {
      super(props);
      this.state = { name: 'foo' };
    }
    render() {
      const { name } = this.state;
      return (
        <div className={name} />
      );
    }
  }
  const wrapper = shallow(<Foo />);
  it('测试 setState 函数', () => {
    expect(wrapper.state('name')).to.equal('foo');
    wrapper.setState({ age: 18 });
    wrapper.setState({ name: 'bar' });
    expect(wrapper.state('name')).to.equal('bar');
    expect(wrapper.state('age')).to.equal(18);
  })

.setProps(nextProps) => ShallowWrapper 手动更新根组件的props
function MyComponent(props) {
    const { includedProp } = props;
    return (
      <div className="foo bar" includedProp={includedProp}>Hello</div>
    );
  }
  const wrapper = shallow(<MyComponent includedProp="Success!" excludedProp="I'm not    included" />);
  it('测试 props 函数', () => {
    expect(wrapper.prop('includedProp')).to.equal('Success!');
    wrapper.setProps({includedProp:'hello'});
    expect(wrapper.prop('includedProp')).to.equal('hello');
  })

.setContext(context) => ShallowWrapper 手动设置根组件的上下文
import PropTypes from 'prop-types'; 

function SimpleComponent(props, context) {
    const { name } = context;
    return <div>{name}</div>;
  }
  SimpleComponent.contextTypes = { // 必须这样写
    name: PropTypes.string,
  };
  const context = { name: 'foo' };
  const wrapper = shallow(<SimpleComponent />, { context });
  it('测试 .setContext 函数', () => {
    expect(wrapper.text()).to.equal('foo');
    wrapper.setContext({name:'bar'});
    expect(wrapper.text()).to.equal('bar');
  })

.instance() => ReactComponent 返回根组件的实例,返回测试组件的实例
 function Stateless() {
    return <div>Stateless</div>;
  }

  class Stateful extends React.Component {
    render() {
      return <div>Stateful</div>;
    }
  }
  it('测试 .instance 函数在无状态组件中', () => {
    const wrapper = shallow(<Stateless />);
    const instance = wrapper.instance();
    expect(instance).to.equal(null);
  })
  it('测试 .instance 函数在类组件中', () => {
    const wrapper = shallow(<Stateful />);
    const instance = wrapper.instance();
    expect(instance).to.be.instanceOf(Stateful);
  })

.update() => ShallowWrapper 在根组件实例上调用.forceUpdate()
class ImpureRender extends React.Component {
    constructor(props) {
      super(props);
      this.count = 0;
    }
    render() {
      this.count += 1;
      return <div>{this.count}</div>;
    }
  }
  it('测试 update 函数', () => {
    const wrapper = shallow(<ImpureRender />);
    wrapper.update();
    expect(wrapper.text()).to.equal('1');
  })

.debug() => String 返回当前浅渲染树的字符串表示形式,以便进行调试
function Book({ title, pages }) {
  return (
    <div>
      <h1 className="title">{title}</h1>
      {pages && (
        <NumberOfPages
          pages={pages}
          object={{ a: 1, b: 2 }}
        />
      )}
    </div>
  );
}
Book.propTypes = {
  title: PropTypes.string.isRequired,
  pages: PropTypes.number,
};
Book.defaultProps = {
  pages: null,
};

const wrapper = shallow(<Book title="Huckleberry Finn" />);
console.log(wrapper.debug());
.type() => String|Function 返回包装器(wapper)的当前节点的类型。
 function Foo() {
    return <div />
  };
  function Bar() {
    return <Foo />
  };
  it('测试 .type 函数', () => {
    const wrapper = shallow(<Foo />);
    expect(
      wrapper.type()
    ).to.equal('div')
    const wrapper = shallow(<Bar />);
    expect(
      wrapper.type()
    ).to.equal(Foo)
    function Null() {
      return null;
    }
    const wrapper = shallow(<Null />);
    expect(
      wrapper.type()
    ).to.equal(null);
  })

.name() => String 返回当前节点的名称
function Foo() {
    return <div>
      <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
      </ul>
    </div>
  }
  function Bar() {
    return <Foo />
  }
  const wrapper = shallow(<div />);
  expect(wrapper.name()).to.equal('div');
  const wrapper = shallow(<Foo />);
  expect(wrapper.name()).to.equal('div');
  const wrapper = shallow(<Bar />);
  expect(
    wrapper.name()
  ).to.equal('Foo');

function Foo() {
    return <div>
      <ul>
        <li>1</li>
        <li>1</li>
        <li>1</li>
      </ul>
    </div>
  }
  function Bar() {
    return <Foo />
  }
  it('测试 .name 函数', () => {
    Foo.displayName = "hello world";
    function Bar() {
      return <Foo />
    }
    const wrapper = shallow(<Bar />);
    expect(
      wrapper.name()
    ).to.equal('hello world')
  })

.forEach(fn) => ShallowWrapper 迭代当前的每个节点并执行提供的函数
const wrapper = shallow((
    <div>
      <div className="foo" >123</div>
      <div className="foo" >123</div>
      <div className="foo" >123</div>
    </div>
  ))
  it('测试 .forEach 函数', () => {
    wrapper.find('.foo').forEach((node) => {
      expect(node.text()).to.equal('123');
      expect(node.hasClass('foo')).to.equal(true);
    })
  })

.map(fn) => Array 将当前的节点数组映射到另一个数组
const wrapper = shallow((
    <div>
      <div className="foo">bax</div>
      <div className="foo">bar</div>
      <div className="foo">baz</div>
    </div>
  ));
  it('测试 .map 函数', () => {
    const texts = wrapper.find('.foo').map(node => node.text());
    expect(texts).to.eql(['bax', 'bar', 'baz']);
  })

.reduce(fn[, initialValue]) => Any 将当前节点数组减少为一个值
function Bar(props) {
    const { amount } = props;
    return <div>{amount}</div>
  }
  function Foo() {
    return (
      <div>
        <Bar amount={2} />
        <Bar amount={4} />
        <Bar amount={8} />
      </div>
    );
  }
   it('测试 .reduce 函数', () => {
    const wrapper = shallow(<Foo />);
    const total = wrapper.find(Bar).reduce((amount, n) => amount + n.prop('amount'), 0);
    expect(total).to.equal(14);
  })

.reduceRight(fn[, initialValue]) => Any 将当前节点数组从右到左减少为一个值
function Foo() {
  return (
    <div>
      <Bar amount={2} />
      <Bar amount={4} />
      <Bar amount={8} />
    </div>
  );
}
const wrapper = shallow(<Foo />);
const total = wrapper.find(Bar).reduceRight((amount, n) => amount + n.prop('amount'), 0);
expect(total).to.equal(14);

.slice([begin[, end]]) => ShallowWrapper 根据Array#slice的规则返回具有原始包装器的节点的子集的新包装器。
const wrapper = shallow((
  <div>
    <div className="foo bax" />
    <div className="foo bar" />
    <div className="foo baz" />
  </div>
));
expect(wrapper.find('.foo').slice(1)).to.have.lengthOf(2);
expect(wrapper.find('.foo').slice(1).at(0).hasClass('bar')).to.equal(true);
expect(wrapper.find('.foo').slice(1).at(1).hasClass('baz')).to.equal(true);

const wrapper = shallow((
  <div>
    <div className="foo bax" />
    <div className="foo bar" />
    <div className="foo baz" />
  </div>
));
expect(wrapper.find('.foo').slice(1, 2)).to.have.lengthOf(1);
expect(wrapper.find('.foo').slice(1, 2).hasClass('bar')).to.equal(true);

.tap(intercepter) => Self 点击wrapper方法链。有助于调试。
const result = shallow((
  <ul>
    <li>xxx</li>
    <li>yyy</li>
    <li>zzz</li>
  </ul>
)).find('li')
  .tap(n => console.log(n.debug()))
  .map(n => n.text());

.some(selector) => Boolean 返回 是否有 节点与提供的选择器匹配。
const wrapper = shallow((
    <div>
      <div className="foo qoo" />
      <div className="foo boo" />
      <div className="foo hoo" />
    </div>
  ));
  it('测试 .some 函数',() => {
    expect(wrapper.find('.foo').some('.qoo')).to.equal(true)
    expect(wrapper.find('.foo').some('.bar')).to.equal(false)
  })

.someWhere(predicate) => Boolean 返回 是否有 节点 传递所提供的断言函数。
const wrapper = shallow((
    <div>
      <div className="foo qoo" />
      <div className="foo boo" />
      <div className="foo hoo" />
    </div>
  ));
  it('测试 .someWhere 函数', () => {
    expect(wrapper.find('.foo').someWhere(node => node.hasClass('hoo'))).to.equal(true);
    expect(wrapper.find('.foo').someWhere(node => node.hasClass('bar'))).to.equal(false);
  })

.every(selector) => Boolean 返回 是否 有节点与提供的选择器匹配。
 const wrapper = shallow((
    <div>
      <div className="foo qoo" />
      <div className="foo boo" />
      <div className="foo hoo" />
    </div>
  ));
  it('测试 .someWhere 函数', () => {
    expect(wrapper.find('.foo').every('.foo')).to.equal(true);
    expect(wrapper.find('.foo').every('.qoo')).to.equal(false);
  })

.everyWhere(predicate) => Boolean 返回 是否 所有 节点都传递所提供的断言函数。
const wrapper = shallow((
    <div>
      <div className="foo qoo" />
      <div className="foo boo" />
      <div className="foo hoo" />
    </div>
  ));
  it('测试 .someWhere 函数', () => {
    expect(wrapper.find('.foo').everyWhere(node => node.hasClass('foo'))).to.equal(true);
    expect(wrapper.find('.foo').everyWhere(node => node.hasClass('qoo'))).to.equal(false);
  })

.dive([options]) => ShallowWrapper 浅渲染当前wrapper的一个非DOM子元素,并在结果周围返回一个wrapper
 function Bar() {
    return (
      <div>
        <div className="in-bar">123123</div>
      </div>
    );
  }
  function Foo() {
    return (
      <div>
        <Bar />
      </div>
    );
  }
  it('测试 .dive 函数',() => {
    const wrapper = shallow(<Foo />);
    expect(
      wrapper.find('.in-bar')
    ).to.have.lengthOf(0)
    expect(
      wrapper.find(Bar).dive().find('.in-bar').text()
    ).to.equal('123123')
  })

.hostNodes => ShallowWrapper 查找主节点时,默认查找的是 HTML 标签而不是 React components
/* Counter2component.js */
class Counter2 extends React.Component{
    render(){
        return(
            <div>
                <Box className="foo" />
                <span className="foo"></span>
                <span className="foo"></span>
                <span className="foo"></span>
            </div>
        )
    }
}

/* test/components.test.js */
describe('测试 Counter2Component 文件',() => {
    it('主节点是 HTML 而不是 React 组件', () => {
        const wrapper = shallow(<Counter2 />)
        const twoNodes = wrapper.find(".foo");
        expect(
            twoNodes.hostNodes()
        ).to.have.lengthOf(3)
  })
})

覆盖率

修改package.json添加执行命令

"scripts": {
    "test": "umi-test --coverage"
 },

然后执行npm run test 跑单元测试的在跟目录会生成一个coverage目录,

通过打开这个HTML文件再浏览器查看 就可以看到整个项目的测试报告了

12.jpg

如:

6.jpg
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容