Articles19
Tags0
Categories0

测试案例

测试案例

一.关于shallow与DOM

shallow:只有属性没有状态,不渲染子组件

DOM:组件,子组件,生命周期函数

二.单元测试

1.测试在一个数组渲染的列表中项的属性

it('should render correct icon amd price for each item',()=>{
    const iconList=wrapper.find('.list-group-item').first().find(Ionicon)
    //第一个节点
    expect (iconList.length).toEqual(3)
    expect(iconList.first().props().icon).toEqual(itemsWithCategory[0].category.iconName)
    //取属性:.props()
  })

2.利用模拟函数的测试

定义:

const props ={
  onModifyItem:jest.fn(),//模拟函数方法
  onDeleteItem:jest.fn()
}

触发:

 firstItem.find('a').first().simulate('click')

测试是否调用(有参数):

expect(props.onDeleteItem).toHaveBeenCalledWith(itemsWithCategory[0])

整体:以列表项操作为例,一个a标签是编辑,第二个a是删除操作

 it('should trigger the correct function callback',()=>{
    const firstItem=wrapper.find('.list-group-item').first()
    firstItem.find('a').first().simulate('click')
    expect(props.onModifyItem).toHaveBeenCalledWith(itemsWithCategory[0])
    firstItem.find('a').last().simulate('click')
    expect(props.onDeleteItem).toHaveBeenCalledWith(itemsWithCategory[0])
  })

测试中字符(.text())转化为数字(.text()*1)

 expect(wrapper.find('.income span').text()*1).toEqual(1000)

3.模拟componentDidMount中的事件监听

(此处实现点击按钮,下拉框出现,点击下拉框以外的地方,下拉框消失):

 componentDidMount(){
    document.addEventListener('click',this.handleClick,false)
    //监听点击事件,一有监听到,执行回调函数handleClick
    }
    //组件关闭时清除事件监听
    componentWillUnmount(){
    document.removeEventListener('click',this.handleClick,false)
  }
   handleClick=(event)=>{
    if(this.node.contains(event.target)){
      return
    }
    //this.node为绑定ref处
    this.setState({
      isOpen:false
     })
  }
  ....
  render(){
  return(
    <div className="dropdown month-picker-component"  ref={(ref)=>{this.node=ref}}>
    ....
    </div>
  )
  }

测试:

it('after the dropdown is shown, click the document should close the dropdown', () => {
let eventMap={}
document.addEventListener=jest.fn((event,cb)=>{
eventMap[event]=cb
})
// 用jest.fn():自定义模拟函数方法,cb是回调函数,一旦触发,就把回调函数赋予eventMap对象
const wrapper=mount(<MonthPicker{...props}/>)
wrapper.find('.dropdown-toggle').simulate('click')
//模拟点击出现下拉框
 expect(wrapper.state('isOpen')).toEqual(true)
 expect(wrapper.find('.dropdown-menu').length).toEqual(1)
 //模拟点击特定的节点
 eventMap.click({
 target: ReactDOM.findDOMNode(wrapper.instance())
 //节点为wrapper,即<MonthPicker{...props}/>
 })
 expect(wrapper.state('isOpen')).toEqual(true)
 eventMap.click({
 target: document,
 //页面其他节点
 }) 
  expect(wrapper.state('isOpen')).toEqual(false)
})

二.集成测试(容器页面级的测试)

因为用测试到子组件,需要用mount

1.测试默认状态:

describe('test Home container component',()=>{
  const wrapper=mount(<Home/>)
  it('should render the default layout',()=>{
    const currentDate=parseToYearAndMonth('2019/01/01')
    expect(wrapper.find(PriceList).length).toEqual(1)
    expect(wrapper.find(ViewTab).props().activeTab).toEqual(LIST_VIEW)
    expect(wrapper.find(MonthPicker).props().year).toEqual(currentDate.year)
    expect(wrapper.find(PriceList).props().items.length).toEqual(2)
  })

2.模拟事件

 //此处模拟的是点击tab标签,列表页消失,图表页出现
it('click the another view tab,should change the default view',()=>{
    wrapper.find('.nav-item a').last().simulate('click')
    expect(wrapper.find(PriceList).length).toEqual(0)
    expect(wrapper.find('.chart-title').length).toEqual(1)
  })
  //此处模拟日期下拉列表点击年份
   it('click the new month item,should switch to the correct items ',()=>{
    wrapper.find('.nav-item a').first().simulate('click')
    //点击日期选择按钮,出现下拉列表
wrapper.find('.dropdown-toggle').simulate('click')
//点击第4个月份 at(4)
wrapper.find('.months-range .dropdown-item').at(3).simulate('click')
expect(wrapper.find(MonthPicker).props().month).toEqual(4)
expect(wrapper.find(PriceList).props().items.length).toEqual(1)
  })
  //模拟新建列表项
  it('click the create button,should create the new item',()=>{
    wrapper.find(CreateBtn).simulate('click')
    expect(wrapper.find(PriceList).props().items.length).toEqual(2)
    expect(wrapper.state('items')[0]).toEqual(newItem)
  })

3.一个完整的例子

测试一个create页面,这个页面可以根据路由(/create或/edit)决定是新增项页面还是编辑页面,内容包括类别选择和表单(标题,价格,时间),表单提交后跳转首页,编辑的话从match中拿项目id

import React from 'react'
import { mount } from 'enzyme'
// MemoryRouter路由历史不通过URL缓存,而是保存在内存中
//也就是说,地址栏地址不变,而在内存中保存路由信息,当浏览器刷新时,自动跳回路由首页
//还是可以通过访问location对象得到路由信息
import { MemoryRouter } from 'react-router-dom'
import  Create   from '../Create'
import { parseToYearAndMonth, flatternArr, TYPE_OUTCOME } from '../../utility'
import Loader from '../../components/Loader'
import CategorySelect from '../../components/CategorySelect'
import PriceForm from '../../components/PriceForm'
import { testCategories, testItems } from '../../testData'
//testData为静态测试数据




const testItem=testItems[0]
const match={params:{id:testItem.id}}
//history模拟跳转
const history={push:()=>{}}
const createMatch={params:{id:''}}
//初始状态
const initData={
  categories:{},
  items:{},
  isLoading:false,
  currentDate:parseToYearAndMonth()
}
const actions={
  //当编辑页面拿到该项目的数据
  getEditData:jest.fn().mockReturnValue(Promise.resolve({editItem:testItem,categories:flatternArr(testCategories)})),//模拟返回固定值
  //编辑后更新数据
  updateItem:jest.fn().mockReturnValueOnce(Promise.resolve('')),//模拟返回一次空值
  //新增项目
  createItem:jest.fn().mockReturnValueOnce(Promise.resolve(''))
}
//数据加载成功
const withLoadedData={
  categories:flatternArr(testCategories),
  items:flatternArr(testItems),
  isLoading:false,
  currentDate:parseToYearAndMonth()
}
//正在加载数据,数据此时为空
const loadingData={
  ...initData,isLoading:true
}
describe('test component init behavior',()=>{
  it('test Create page for the first render,getEditData should be called with the right params',()=>{
    const wrapper=mount(
      <MemoryRouter>
        <Create data={initData} actions={actions}match={match}/>
      </MemoryRouter>
    )
    expect(actions.getEditData).toHaveBeenCalledWith(testItem.id)
  })
  it('should show loading component when isLoading is true',()=>{
    const wrapper=mount(
      <MemoryRouter>
      <Create data={loadingData}actions={actions}match={match}/>
      </MemoryRouter>
    )
    expect(wrapper.find(Loader).length).toEqual(1)
  })
})
//新增
describe('test component when in create mode',()=>{
  const wrapper=mount(
    <MemoryRouter>
      <Create data={withLoadedData} actions={actions} match={createMatch} history={history} />
    </MemoryRouter>
  )
  //instance创建一个实例
  const setInputValue=(selector,newValue)=>{
    wrapper.find(selector).instance().value=newValue
    }
    it('should pass the null to props selectedCategory for CategorySelect',()=>{
      expect(wrapper.find(CategorySelect).props().selectedCategory).toEqual(null)
    })
    it('should pass empty object for PriceForm',()=>{
      expect(wrapper.find(PriceForm).props().item).toEqual({})
    })
    it('submit the form,the addItem should not be triggered',()=>{
      wrapper.find('form').simulate('submit')
      expect(actions.createItem).not.toHaveBeenCalled()
    })
    it('fill all inputs,and select the category,submit the form,addItem should be called',()=>{
      //#title:id为title的节点
      setInputValue('#title','new title')
      setInputValue('#price','200')
      setInputValue('#date','2018-08-30')
      //模拟点击第一个类别
      wrapper.find('.category-item').first().simulate('click')
      //模拟提交
      wrapper.find('form').simulate('submit')
      const testData={title:'new title',price:200,date:'2018-08-30'}
      expect(actions.createItem).toHaveBeenCalledWith(testData,testCategories[0].id)
    })
  })
  //编辑
  describe('test component when in edit mode',()=>{
    const wrapper=mount(
      <MemoryRouter>
        <Create data={withLoadedData} actions={actions}match={match}history={history}/>

      </MemoryRouter>
    )
    const setInputValue=(selector,newValue)=>{
      wrapper.find(selector).instance().value=newValue
    }
    const selectedCategory=testCategories.find(category=>testItem.cid===category.id)
    it('should pass the right category to props selectedCategory for CategorySelect',()=>{
      wrapper.update()
      expect(wrapper.find(CategorySelect).props().selectedCategory).toEqual(selectedCategory)
    })
    it('modify some inputs submit the form,modifyItem should be called',()=>{
      setInputValue('#title','new title')
      wrapper.find('form').simulate('submit')
      const testData={...testItems,title:'new title'}
      expect(actions.updateItem).toHaveBeenCalledWith(testData,selectedCategory.id)
    })
  })
Author:shuo
Link:http://yoursite.com/2019/10/09/测试案例/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可