测试应该校验数据正确性吗?如何巧妙地进行校验呢?

问题

《测试金字塔的实践》这篇文章中,有这样的说法:

……不要在你的单元测试里耦合实现代码的内部结构。要测试可观测的行为。你应该这样思考:
如果我的输入是 x 和 y,输出会是 z 吗?

就算得出的是“z”(eg. “审核结果已保存”),我怎么确保它内部执行正确呢?

举例

目前我能想到的验证方式如下:

编写充值的测试,在调用前后,验证“获取我的余额”接口的返回数据,以确保我的余额真的增加了。
由此,充值接口与当前“获取余额”造成了测试代码的耦合。

一旦这种情况多了,修改获取余额接口的返回结构,将面对整个相关测试代码的瘫痪。

如此这般,明显不合适...所以到底“正确的测试”该如何编写?到哪个程度才算作合格。

阅读 3.2k
1 个回答

每一个单元测试只是是用来测试一个函数或者功能的正确,至于函数里面怎么实现都可以,只要测试得到想要的结果就可以.
至于要保证函数内部功能的正确,就需要一些模式来实现.
比如要测试一个两数相加的方法.

it('add two num'){
    expect(add(1,2)).toBe(3)
}

为了测试通过你可以直接这么写

function add(a,b){
    return 3
}

测试是通过了,但是这个实现是不是看着很欠抽

这个时候可以用三角法Triangulation
在加一个测试

it('add two num'){
    expect(add(4,5)).toBe(9)
}

这个测试就通不过了
实现代码就得重写,要通过不同参数的测试,就必须保证函数内部逻辑的正确

function add(a,b){
    return a+b
}

然后如果还有别的需求,比如说输入的参数不能为负数,或者不能为小数,就可以添加相应的测试,然后重构方法,保证测试通过
这样一步步通过测试,制造边界,来驱动开发的进行,让功能越来越完善
感觉有点像是玩游戏打怪升级,测试像副本一样,没刷过去心里就不爽,然后熬夜奋战,只是这个副本是我们自己建的


像你举得例子,在测试的时候,并不会跟真实的数据做交互,而是通过模拟的数据来做测试的.
充值接口的测试不需要关心获取余额的事情,获取余额有自己的测试来保证.
所以如果修改余额接口的话,只需要修改获取余额的测试即可.
如果要真实的测试各个接口之间的联动,那就超出单元测试的范围了,属于集成测试和端到端测试

补充:
关于修改,除了最开始的开发阶段,一般是不会去修改原有的测试的,而是添加新的测试,然后在你的功能里面添加对原有功能和新功能的兼容,如果你修改了测试,也就不能保证你的功能和原有的功能兼容,那么之前使用你 api 的人就会非常痛苦.
除非有大版本的升级.

伪代码如下

// recharge.js
import post from './post'
function recharge(money) {
  // 返回 true 或 false
  return post('/recharge', {
    data: { money },
  })
}
// post.js 在测试中不会执行
function post(url, { data }) {
  return http(url, { data })
}
// recharge.test.js
import post from './post'

const deposit = { money: 10 }
// 模拟post方法,替换真实的 api 请求
post.mockImplementation(() => {
  return {
    post: (url, { data }) => {
      deposit.money += data.money
      return true
    },
  }
})
it('测试存款是否增加', () => {
  const money = 10
  recharge(money)
  expect(deposit.money).toBe(20)
})
it('测试是否返回成功', () => {
  const money = 10
  expect(recharge(money)).toBe(true)
})
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题