Articles11
Tags0
Categories0

用JTest和Enzyme测试

用JTest和Enzyme测试

JTest

1.安装(npm install –save-dev jest)后在package.json中配置:

{
  "scripts":

   {"test":"jest"}

}

2.一些常用的语法

普通适配器:

test ('two plus two is four',()=>{

  expect(2+2).toBe(4);

})

对象:

test('object assignment',()=>{

   const daata={one:1}

   data['two']=2;

   expect(data).toEqual({

     one:1,

     two:2

   })

})

递归:

test('adding positive numbers is not zero', () => {
 for (let a = 1; a < 10; a++) {
  for (let b = 1; b < 10; b++) {
    expect(a + b).not.toBe(0);
  }
 }
})

数字比较:

(>3): toBeGreaterThan(3)

(>=3): toBeGreaterOrEqualThan(3)

​ (<3): toBeLessThan(3)

比较浮点数相等:

test('两个浮点数相加',()=>{

  const value=0.1+0.2;

   expect(value).toBeCloseTo(0.3)

})

正则表达式:toMatch

包含(数组,对象)toContain

错误异常:toThrow===》

.toThorw可能够让我们测试被测试方法是否按照预期抛出异常,但是在使用时需要注意的是:我们必须使用一个函数将将被测试的函数做一个包装,正如上面getIntArrayWrapFn所做的那样,否则会因为函数抛出导致该断言失败。

import functions  from '../src/functions';

test('getIntArray(3.3)应该抛出错误', () => {
  function getIntArrayWrapFn() {
    functions.getIntArray(3.3);
  }
  expect(getIntArrayWrapFn).toThrow('"getIntArray"只接受整数类型的参数');
})

toEqual匹配器会递归的检查对象所有属性和属性值是否相等,所以如果要进行应用类型的比较时,请使用.toEqual匹配器而不是.toBe。

.toHaveLength可以很方便的用来测试字符串和数组类型的长度是否满足预期。

3.回调

使用单个参数调用done,jest会等done回调函数执行结束后结束测试

test('the data is peanut better',done=>{

  function callback(data){

    expect(data).toBe('peanut butter');

    done();

  }

fetchData(callback)

})

expect.assertions(1),它能确保在异步的测试用例中,有一个断言会在回调函数中被执行。这在进行异步代码的测试中十分有效。

使用promise执行异步(一定不要忘记把 promise 作为返回值)

test('the fetch fails with an error',()=>{

  return fetchData().then(data=>{

  expevt(data).toBe('peanut butter')

  })

})

4.对一些重复性工作的处理

beforeEach(()=>{

 initializeCityDatabase();

})

afterEach((=>{

  clearCityDatabase();

}))

一次性设置

beforeAll(()=>{

  return initializeCityDatabase();

})

afterAll(() => {
  return clearCityDatabase();
});

作用域:

describe('matching cities to foods',()=>{

  beforeEach(() => {
    return initializeFoodDatabase();
   });
  test('Vienna <3 sausage', () => {
    expect(
    isValidCityFoodPair('Vienna', 'WienerSchnitzel')
     ).toBe(true);
   });
   test('San Juan <3 plantains', () => {
    expect(
    isValidCityFoodPair('San Juan','Mofongo'
    ).toBe(true);
   });

})

Jest 会在所有真正的测试开始之前执行测试文件里所有的 describe 处理程序(handlers)。 这是在 before* 和 after* 处理程序里面 (而不是在 describe 块中)进行准备工作和整理工作的另一个原因。 当 describe 块运行完后,,默认情况下,Jest 会按照 test 出现的顺序

仅运行一个测试:

test.only('this will be the only test that runs',()=>{

  expect(true).toBe(false);

})

5.mock:代码之间的连接:擦除函数的实现,捕获函数的调用等

.mock它保存了关于此函数如何被调用、调用时的返回值的信息。

Mock函数提供的以下三种特性,在我们写测试代码时十分有用:

捕获函数调用情况
设置函数返回值
改变函数的内部实现

=====================

(1).jest.fn:创建Mock函数,如果没有定义函数内部的实现,则返回undefine

toBeCalled:被调用

toBeCalledTimes(1):被调用一次

toHaveBeenCalledWith(1,2,3):传入的参数1,2,3

(2).jest.fn()创建的函数设置返回值,定义内部实现

test("测试jest.fn()返回固定值",()=>{

 let mockFn=jest.fn().mockReturnValue('default');

  expect(mockFn().toBe('default');)

  })

test('测试jest.fn()内部实现',()=>{

  let mockFn=jest.fn((num1,num2)=>{

  return num1*num2

})

expect(mockFn(10,10)).toBe(100)

})

利用async实现异步

//fetch.js
import axios from 'axios';

export default {
  async fetchPostsList(callback) {
    return axios.get('https://jsonplaceholder.typicode.com/posts').then(res => {
      return callback(res.data);
    })
  }
}
import fetch from '../src/fetch.js'

test('fetchPostsList中的回调函数应该能够被调用', async () => {
  expect.assertions(1);
  let mockFn = jest.fn();
  await fetch.fetchPostsList(mockFn);

  // 断言mockFn被调用
  expect(mockFn).toBeCalled();
})

(3)jest.mock():在jest中如果想捕获函数的调用情况,则该函数必须被mock或者spy!

import fetch from './fetch';

export default {
  async getPostList() {
    return fetch.fetchPostsList(data => {
      console.log('fetchPostsList be called!');
      // do something
    });
  }
}
// functions.test.js

import events from '../src/events';
import fetch from '../src/fetch';

jest.mock('../src/fetch.js');

test('mock 整个 fetch.js模块', async () => {
  expect.assertions(2);
  await events.getPostList();
  expect(fetch.fetchPostsList).toHaveBeenCalled();
  expect(fetch.fetchPostsList).toHaveBeenCalledTimes(1);
});

jest.spyOn()方法同样创建一个mock函数,但是该mock函数不仅能够捕获函数的调用情况,还可以正常的执行被spy的函数。实际上,jest.spyOn()是jest.fn()的语法糖,它创建了一个和被spy的函数具有相同内部代码的mock函数。

(4)mock的其他方法:改变mock函数=》mockImplementationOnce

const myMockFn=jest

.fn(()=>'default')

.mockImplementationOnce(()=>'first call')

.mockImplementationOnce(()=>'second call')

console.log(myMockFn(),myMockFn(),myMockFn(),myMockFn())

打印结果:

=====>’first call’,’second call’,’default’,’default’

==========================================

const myObj = {
myMethod: jest.fn().mockReturnThis(),
};
// is the same as
const otherObj = {
myMethod: jest.fn(function() {
return this;
}),
};

=============================================

mock名称:jest.mockName(‘add42’)

// 这个 mock 函数至少被调用一次
expect(mockFunc).toBeCalled();

// 这个 mock 函数至少被调用一次,而且传入了特定参数
expect(mockFunc).toBeCalledWith(arg1, arg2);

// 这个 mock 函数的最后一次调用传入了特定参数
expect(mockFunc).lastCalledWith(arg1, arg2);

// 所有的 mock 的调用和名称都被写入了快照
expect(mockFunc).toMatchSnapshot();

6.快照

(1)安装依赖:npm install react-test-render –save-dev

​ 使用:

import React from 'react'

​           import renderer from 'react-test-renderer'

​           import Alink from '../components/Link.react';

​           it('正确的渲染',()=>{

​            const tree=renderer

​                   .create(<ALink page="https://www.gowhich.com">Gowhich</ALink>)

​                    .toJSON();

​                  expect(tree).toMatchSnapshot();

​           })

​ 重新生成快照

​ npx jest 目录 –notify –watchman=false –updateSnapshot

​ 数据更新:

      it('检查匹配器并测试通过', () => {
  const user = {
    createAt: new Date(),
    id: Math.floor(Math.random() * 20),
    name: 'Durban',
  };

 expect(user).toMatchSnapshot({

   createAt:expect.any(Date),

    id:expect.any(Number)

  })

})

7.定时期

const timerGame =require('../lib/timerGame');

jest.useFakeTimers();

test('等待1秒钟后结束游戏'()=>{

  timerGame();

  expect(setTimeout).toHaveBeenCalledTimes(1);

  expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function),1000)

})

运行所有计时器

const timerGame=require('../lib/timerGame');

jest.useFakeTimers();

test('1秒钟后调用回调callback',()=>{

const callback=jest.fn();

timerGame(callback);

// 在这个时间点上,callback回调函数还没有被调用

expect(callback).not.toBeCalled();

 // 所有timers被执行

jest.runAllTimers();

  // 现在我们的callback回调函数被调用

expect(callback).toBeCalled();

expect(callback).toHaveBeenCalledTimes(1);



})          

运行待定时间器:

runOnlyPendingTimers()

按时间提前计时器

advanceTimersByTime(1000)

添加覆盖率和颜色:”test”:”jest –colors –coverage”(package.json)

Enzyme

1.三种测试方法:

shallow:返回组件的浅渲染(当前组件,虚拟dom)

mount:将组件加载成真实DOM,会渲染当前组件和子组件

render:渲染结果为普通的html结构

2.API:

.get(index):返回指定位置的子组件的Dom节点

.at(index):返回指定位置的子组件

.first():返回第一个组件

.last():返回最后一个组件

.type():返回当前组件的类型

.text():返回当前组件的文本内容

.html():返回当前组件的HTML代码形式

.props():返回根组件的所有属性

.props(key):返回根组件的指定属性

.state([key]):返回根组件的状态

.setState(nextState):设置根组件的状态

.setProps(nextProps):设置根组件的属性

eg:expect (wrapper.find(‘input’).prop(‘value’)).toBe(‘default value’)

3.模拟Props

import {shallow} from  'enzyme'

import React from 'react'

import {OrderManage}from '../../components/purchaser/OrderManege'

const setup={{...props}}=>{

  const wrapper=shallow(<OrderManage{...props}/>);

  return{

     props,

     wrapper,

   };

  };

  describe('OrderManage',()=>{

    it('role is operator',()=>{

    const {wrapper}=setup({

    role:'operator',

    isFetching:true,

    fetchOrdersByStatuses:()=>{},//直接设为空函数

    getData:jest.fn(),//Jest提供的mock函数

    });

const params={

   node:{

   id:2,

   },

 };

expect(wrapper.instance().handlePageChange(1));

expect(wrapper.instance().OrderManagementLink(params));

expect(wrapper.find('.loader')).toHaveLength(1);

expect(wrapper.find('.order-simpleGrid')).toHaveLength(0);

expext(wrapper.type()).toEqual('div')

  })

})

//instance:获取组件的内部成员对象

组件中的方法测试:

//组件

export class Card extends React.Component{

  constructor(props){

    super(props)

    this.cardType='initCard'

   }

 changeCardType(cardType){

    this.cardType=cardType

 }

}

//test

it('changeCardType',()=>{

  let component =shallow(<Card/>)

  expect(component.instance().cardType).toBe('initCard')

  component.instance().changeCardType('testCard')

  expect(component.instance().cardType).toBe('testCard')

})

4.模拟事件测试

<input value={value} onChange={e=>this.handleChange(e)}/>

//test

it('can save value and cancel',()=>

  const value='edit'

  const {wrapper,props}=setup{{

  editable:true

 }}

wrapper.find('input').simulate('change',{target:{value}});

wrapper.setProps({status:'save'});

expect(props.onChange).toBeCalledWith(value)

})

==========================

wrapper.find(‘button’).simulate(‘click’);

wrapper.find(‘input’).simulate(‘keyup’);

expect(props.onClick).toBeCalled();// onClick方法被调用

expect(props.onClick).not.toBeCalled() // onClick方法没被调用

import { shallow } from 'enzyme';
import sinon from 'sinon';
import Foo from './Foo';

describe('<MyComponent />', () => {
  it('renders three <Foo /> components', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.find(Foo)).to.have.lengthOf(3);
  });

  it('renders an `.icon-star`', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.find('.icon-star')).to.have.lengthOf(1);
  });

  it('renders children when passed in', () => {
    const wrapper = shallow((
      <MyComponent>
        <div className="unique" />
      </MyComponent>
    ));
    expect(wrapper.contains(<div className="unique" />)).to.equal(true);
  });

  it('simulates click events', () => {
    const onButtonClick = sinon.spy();
    const wrapper = shallow(<Foo onButtonClick={onButtonClick} />);
    wrapper.find('button').simulate('click');
    expect(onButtonClick).to.have.property('callCount', 1);
  });
});
Author:shuo
Link:http://yoursite.com/2019/08/28/JTest/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可