【全栈React】第25天: 使用Enzyme做更好的测试

前端之家收集整理的这篇文章主要介绍了【全栈React】第25天: 使用Enzyme做更好的测试前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

本文转载自:众成翻译
译者:iOSDevLog
链接http://www.zcfy.cc/article/3806
原文:https://www.fullstackreact.com/30-days-of-react/day-25/

今天,我们将看看一个由Airbnb所维护的开源库,名为Enzyme,使得测试变得简单易用。

昨天我们使用了react-addons-test-utils 库来编写我们对Timeline 组件的第一个测试。但是,此库是相当低级的,使用起来可能有点麻烦。Enzyme是由 AirBnb 团队发布和维护的测试实用程序库,它提供了一个更好的、高级的 API 来处理测试中的React组件。

我们在测试我们的 <Timeline />组件:

使用Enzyme

我们将使用Enzyme,使这些测试更容易写和更可读。

昨天,我们写了我们的第一个测试如下:

import React from 'react';
import TestUtils from 'react-addons-test-utils';

import Timeline from '../Timeline';

describe('Timeline',() => {

  it('wraps content in a div with .notificationsFrame class',() => {
    const wrapper = TestUtils.renderIntoDocument(<Timeline />);
    TestUtils
      .findRenderedDOMComponentWithClass(wrapper,'notificationsFrame');
  });

})

虽然这是可行的,但它不是世界上最容易阅读的测试。当用Enzyme我们重写它时让我们看看这个测试的样子。

我们可以只测试组件的输出,而不是用Enzyme来测试完整的组件树。将不渲染任何组件的子级。这称为 渲染。

Enzyme使浅渲染超容易。我们将使用Enzyme导出的shallow 函数来装载我们的组件。

让我们更新src/components/Timeline/__tests__/Timeline-test.js 文件,包括enzyme导入shallow 函数:

import React from 'react';
import { shallow } from 'enzyme';

describe('Timeline',() => {
  it('wraps content in a div with .notificationsFrame class',() => {
    // our tests
  });
})

react-addons-test-utils支持浅渲染。事实上,Enzyme素只是包装这个函数。虽然昨天我们没有使用浅渲染,但如果我们使用它看起来像这样:

> const renderer = ReactTestUtils.createRenderer();
> renderer.render(<Timeline />)
> const result = renderer.getRenderOutput(); 
>

现在,为了渲染我们的组件,我们可以使用shallow 方法并将结果存储在一个变量中。然后,我们将为在其虚拟 dom 中渲染的不同的React元素 (HTML 或子组件) 查询 渲染的组件。

整个断言包括两行:

import React from 'react';
import { shallow,mount } from 'enzyme';

import Timeline from '../Timeline';

describe('Timeline',() => {
  let wrapper;

  it('wraps content in a div with .notificationsFrame class',() => {
    wrapper = shallow(<Timeline />);
    expect(wrapper.find('.notificationsFrame').length).toEqual(1);
  });

  it('has a title of Timeline',() => {
    wrapper = mount(<Timeline />)
    expect(wrapper.find('.title').text()).toBe("Timeline")
  })

  describe('search button',() => {
    let search;
    beforeEach(() => wrapper = mount(<Timeline />))
    beforeEach(() => search = wrapper.find('input.searchInput'))

    it('starts out hidden',() => {  
      expect(search.hasClass('active')).toBeFalsy()
    })
    it('becomes visible after being clicked on',() => {
      const icon = wrapper.find('.searchIcon')
      icon.simulate('click')
      expect(search.hasClass('active')).toBeTruthy()
    })
  })

  describe('status updates',() => {
    it('has 4 status updates at minimum',() => {
      wrapper = shallow(<Timeline />)
      expect(
        wrapper.find('ActivityItem').length
      ).toBeGreaterThan(3)
    })
  })

})

我们可以使用yarn test命令 (或 npm test 命令) 一样的方式运行测试:

yarn test

我们的测试通过,并且更易于阅读和维护。

让我们继续写断言,从我们昨天开始的假设列表中抽取。我们将首先构建我们的测试套件的其余部分,写出我们的describeit 块。我们将填写的规格与断言后:

import React from 'react';
import { shallow } from 'enzyme';

import Timeline from '../Timeline';

describe('Timeline',() => {
    wrapper = shallow(<Timeline />);
    expect(wrapper.find('.notificationsFrame').length).toEqual(1);
  });

  it('has a title of Timeline')

  describe('search button',() => {
    it('starts out hidden')
    it('becomes visible after being clicked on')
  })

  describe('status updates',() => {
    it('has 4 status updates at minimum')
  })

})

如果我们遵循测试驱动开发 (简称 TDD),我们将首先编写这些假设,然后构建组件以通过这些测试。

让我们填写这些测试,以便它们通过我们现有的Timeline 组件。

我们的标题测试比较简单。我们将查找标题元素并确认标题Timeline

我们希望标题可以在 .title类下使用。因此,要在规范中使用 .title 类,我们只需使用Enzyme所暴露的find 函数即可获取组件。

因为我们的Header组件是 Timeline 组件的子组件,所以不能使用shallow() 方法。相反,我们必须使用Enzyme提供的 mount() 方法

Shallow? Mount?

shallow() 渲染函数只渲染我们专门测试的组件,它不会渲染子元素。相反,我们将不得不mount() 组件,因为子组件Header 不可用的 jsdom,否则。

我们将在本文的末尾看到更多的Enzyme函数

现在让我们填写标题描述:

import React from 'react';
import { shallow,() => {
    wrapper = mount(<Timeline />) // notice the `mount`
    expect(wrapper.find('.title').text()).toBe("Timeline")
  })
})

运行我们的测试,我们将看到这两个期望通过:

接下来,让我们更新我们的搜索按钮测试。我们在这里有两个测试,其中一个要求我们测试一个交互。Enzyme为处理相互作用提供了一个非常干净的界面。让我们来看看如何根据搜索图标编写测试。

同样,由于我们在时间轴中对子元素进行测试,因此我们必须mount() 元素。因为我们要在一个嵌套的describe()块中编写两个测试,所以我们可以在帮助器之前编写一个新的 mount() 来为每个测试重新创建,这样它们是纯的。

此外,我们还将使用 input.searchInput 元素进行两个测试,因此,让我们在前面的帮助器中为该元素编写.find()

describe('Timeline',() => {
  let wrapper;
  // ...
  describe('search button',() => {
    let search;
    beforeEach(() => wrapper = mount(<Timeline />))
    beforeEach(() => search = wrapper.find('input.searchInput'))
    // ...
  })
})

若要测试是否隐藏了搜索输入,我们只需要知道是否应用了active 类。Enzyme为我们提供了一种使用 hasClass() 方法检测组件是否有类的方法。让我们填写第一个测试,期望搜索输入没有活动类:

describe('Timeline',() => {  
      expect(search.hasClass('active')).toBeFalsy()
    })
    it('becomes visible after being clicked on')
    // ...
  })
})

关于第二个测试的棘手部分是,我们需要点击图标元素。在我们看如何做到这一点之前,让我们先找到它。我们可以在包装上的目标通过它的 .searchIcon 类定位到它。

it('becomes visible after being clicked on',() => {
  const icon = wrapper.find('.searchIcon')
})

现在,我们有了图标,我们想模拟一个点击元素。回想一下,onClick() 方法实际上只是浏览器事件的门面。即,单击一个元素只是一个通过组件冒泡的事件。而不是控制鼠标或调用元素上的click,我们将模拟发生在它上的事件。对我们来说,这将是click 事件。

我们将在icon 上使用simulate() 方法来创建此事件:

it('becomes visible after being clicked on',() => {
  const icon = wrapper.find('.searchIcon')
  icon.simulate('click')
})

现在我们可以设定一个search 组件具有active 类的期望。

it('becomes visible after being clicked on',() => {
  const icon = wrapper.find('.searchIcon')
  icon.simulate('click')
  expect(search.hasClass('active')).toBeTruthy()
})

我们对Timeline 组件的最后期望是至少有四状态更新。当我们将这些元素放置在Timeline 组件上时,我们可以 "浅" 渲染组件。此外,由于每个元素都是自定义组件,因此我们可以搜索'ActivityItem'类型的特定组件的列表。

describe('status updates',() => {
  it('has 4 status updates at minimum',() => {
    wrapper = shallow(<Timeline />)
    // ... 
  })
})

现在,我们可以测试ActivityItem 组件列表的长度。我们将设定我们的期望,如果长度至少是4的名单。

describe('status updates',() => {
    wrapper = shallow(<Timeline />)
    expect(
      wrapper.find('ActivityItem').length
    ).toBeGreaterThan(3)
  })
})

我们现在的整个测试套件如下所示:

import React from 'react';
import { shallow,() => {
      wrapper = shallow(<Timeline />)
      expect(
        wrapper.find('ActivityItem').length
      ).toBeGreaterThan(3)
    })
  })

})

[](#whats-the-deal-with-find)find()处理什么?

在我们结束今天之前,我们应该看看一个Enzyme"渲染的界面 (在我们的测试中,wrapper 的对象)。Enzyme文档 太棒了,所以我们要保持这个简短。

基本上,当我们使用find() 函数时,我们会将它传递给一个选择器,它将返回一个ShallowWrapper 实例来包装找到的节点。find() 函数可以取字符串、函数或对象。

当我们将字符串传递给find()函数时,我们可以传递 CSS 选择器或组件的 _显示名称_。例如:

wrapper.find('div.link');
wrapper.find('Link')

我们还可以将它传递给组件构造函数,例如:

import { Link } from 'react-router';
// ...
wrapper.find(Link)

最后,我们还可以传递对象属性选择器对象,它通过键和值来选择元素。例如:

wrapper.find({to: '/login'});

返回值是一个 ShallowWrapper,它是一种ShallowWrapper类型 (我们可以渲染包装和浅包装)。这些 Wrapper 实例有一组功能,我们可以使用这些函数来针对不同的子组件,查看 propsstate,的方法,以及渲染的组件的其他属性,如html()text()。更甚的是,我们可以把这些调用串在一起。

<Link />组件为例。如果我们想找到基于所有可用链接链接类的 HTML,我们可以编写这样的测试:

// ...
it('displays a link tag with the Login text',() => {
  link = wrapper
        .find('Link')
        .find({to: '/login'})

  expect(link.html())
    .toBe('<a class="link">Login</a>')
});

哦!今天有很多新的信息,但是看看我们是如何快速地用Enzyme来编写后续测试的。阅读的速度要快得多,而且更容易辨别实际发生的事情。

明天,我们将继续我们的测试旅程和通过集成测试测试我们的应用。

原文链接:https://www.f2er.com/react/303202.html

猜你在找的React相关文章