unit test

关于前端测试,有人说测功能很傻,因为业务需求一直在变,所有业务代码都测要耗费很多时间,对于通用的util,通用的组件我们可以进行代码测试。

测试的诸多利好:

  • 使得代码容易被读懂
    在2B的项目里,耗费大量的时间进行数据转换,比如后端给的格式跟前端展示的不同,中间业务的需要,会插入其他数据等等。自己当时消化理解了业务,经过多个函数的多次处理得到最终结果,但是过段时间发现自己写的代码看不懂了,咋办?如果别人维护你的代码,或者你维护别人的代码,大家都很头疼,怎样才能写出让人易读的代码?其实代码就是个管道,有用的流经管道的数据。如果我们知道每个函数的输入的数据,再知道输出数据,串联起来就是整个数据流,这样就很好明白代码的意思。js是弱类型,看不到他的输入格式,也不知道他的输出,此时想到了typescript,熟悉ts也还好,但是更加直观的方式是直接看到每层的数据。那么unit test上场了。比如jest,通过断言查看函数的输入输出


    unit test_第1张图片
    get arguments by function name

    运行测试用例,直观的感受selectItems经过getArgsByFnName处理得到的结果是够跟预期的expectedGetPathRet数据相等。

  • 测试驱动开发
    比如需要处理数据,函数的输入跟你要的结果都是知道的,中间的过程比较复杂,可能一次无法完成,写完了该函数想知道能得到预期的结果没,如果直接将该函数在页面的业务里调用,因为业务比较复杂,环境没那么纯净,unit test 是最纯净的方式,此时写出你的测试用例,输入输出都写出来,最后跑下测试用例一目了然,结合vscode甚至可以debug测试用例,多么美好的一件事情。达到预期的结果再在业务代码里调用。

jest

1.toEqual

递归检查对象或数组的每个字段


unit test_第2张图片
toEqual

对象属性不同,值相同是可以通过的,而数组的元素位置不同就无法通过了

2. create-react-app@3 css module 样式怎么断言?

package.json 我用的是less 所以做下面的配置

    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss|less)$": "identity-obj-proxy"
    },

.test.js

import styles from "../valuesWidget.module.less";

  expect(wrapper.find(`.${styles.valuesWidgetWrapper}`)).toHaveClassName(
    "valuesWidgetWrapper"
  );

-- Local scoped css #201

3. jest链式断言

有时候想对同个变量做多次断言,比如

it('add 1 and 1', () => {
  const actual = 1 + 1;
  expect(actual).toBe(2);
  expect(actual).toBeGreaterThan(1);
  expect(actual).toBeLessThan(3);
});

这样写好麻烦,要是能直接链式调用就好了,jest-chain为我们实现了这个想法

it('add 1 and 1', () => {
  expect(1 + 1)
    .toBe(2)
    .toBeGreaterThan(1)
    .toBeLessThan(3);
});

有时候觉得jest内置的断言语句不够用,试试awesome-jest为我们列出的其他断言库吧,比如jest-extended,再如jest-enzyme结合enzyme对组件进行断言测试。

4. 递归断言对象数组

const state = [
  { type: 'START', data: 'foo' },
  { type: 'START', data: 'baz' },
  { type: 'END', data: 'foo' },
]

… and you want to test that it contains an item like this { type: 'END' } .
You could write the following test:

expect(state).toEqual(          // 1
  expect.arrayContaining([      // 2
    expect.objectContaining({   // 3
      type: 'END'               // 4
    })
  ])
)

The above reads as:

you expect that your Array equals
an Array that contains
an Object that contains
the following properties
NOTE: expect.arrayContaining() must be passed an Array[] even if you pass one item.

-- Jest matching objects in array

expect.arrayContaining expect.objectContaining 结合使用 达到递归测试的目的

toMatchObject也可以递归测试对象

const houseForSale = {
  bath: true,
  bedrooms: 4,
  kitchen: {
    amenities: ['oven', 'stove', 'washer'],
    area: {
      width: 20,
      height: 30,
    },
    wallColor: 'white',
  },
};
const desiredHouse = {
  area: {
    width: 20,
    height: 30,
  }
};

test('the house has my desired features', () => {
  expect(houseForSale).toMatchObject(desiredHouse);
});

-- Allow toMatchObject to match recursively on any depth of an object #3326

test('Array with Object Containing', () => {
  expect(
    [
      { a: 1, b: 2 }, 
      { c: 1, d: 1 }
    ])
    .toContainEqual(
      expect.objectContaining({ a: 1, b: expect.anything() })
    )
})

-- Array.toContain() with expect.objectContaining

但是toMatchObjectobjectContaining还是有差别的

// objectContaining, with nested object, containing full props/values
  // PASSES
  expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
    position: {
      x: expect.any(Number),
      y: expect.any(Number)
    }
  }));

  // objectContaining, with nested object, containing partial props/values
  // FAILS
  expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
    position: {
      x: expect.any(Number)
    }
  }));

  // objectContaining, with nested object, also declared with objectContaining, containing partial props/values
  // PASSES
  expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
    position: expect.objectContaining({
      x: expect.any(Number)
    })
  }));

  // toMatchObject, with nested object, containing full props/values
  // PASSES
  expect({ position: { x: 0, y: 0 } }).toMatchObject({
    position: {
      x: expect.any(Number),
      y: expect.any(Number)
    }
  });

  // toMatchObject, with nested object, containing partial props/values
  // PASSES
  expect({ position: { x: 0, y: 0 } }).toMatchObject({
    position: {
      x: expect.any(Number)
    }
  });

-- What's the difference between '.toMatchObject' and 'objectContaining'

你可能感兴趣的:(unit test)