很多项目经理不愿意执行单元测试,因为这个东西的收益,很难用鞭子抽出来。
因为单元测试很容易进入特定的陷阱。
考虑如下代码(随便从Linux Kernel中拷贝出来的):
static int hclge_get_vector(struct hnae3_handle *handle, u16 vector_num, struct hnae3_vector_info *vector_info) { struct hclge_vport *vport = hclge_get_vport(handle); struct hnae3_vector_info *vector = vector_info; struct hclge_dev *hdev = vport->back; int alloc = 0; int i, j; vector_num = min(hdev->num_msi_left, vector_num); for (j = 0; j < vector_num; j++) { for (i = 1; i < hdev->num_msi; i++) { if (hdev->vector_status[i] == HCLGE_INVALID_VPORT) { vector->vector = pci_irq_vector(hdev->pdev, i); vector->io_addr = hdev->hw.io_base + HCLGE_VECTOR_REG_BASE + (i - 1) * HCLGE_VECTOR_REG_OFFSET + vport->vport_id * HCLGE_VECTOR_VF_OFFSET; hdev->vector_status[i] = vport->vport_id; hdev->vector_irq[i] = vector->vector; vector++; alloc++; break; } } } hdev->num_msi_left -= alloc; hdev->num_msi_used += alloc; return alloc; } |
错误的测试考量是,这个函数基于i, j做了两重循环,所以准备一个hdev的一个vector_status数组,,然后让它进行循环,跑起来,看看覆盖率达到没有,有没有出现死机,如果有修正测试桩,测试完成,覆盖率,用例数,完全满足要求,工作完美完成,收工领饭盒。
正确的考量是,先看懂这个代码是干什么的:它的原理是在hdev中有个数组,里面有一个域表示这个数组成员是否有效,如果无效,就分配出去(并置为有效),相同的方法分配多次,直到完成整个分配过程。
好了,知道这个要点,就要忘掉这个代码了。单元测试的目的是发现“肉眼review看不见的错误”,这种错误主要是那些循环,跳出,判断的时候有没有想错了的东西,那种东西要靠各种边界用例来发现。比如这样:
1) 在vector_status中预设10个成员,4个有效:然后分配0个,1个,3个,4个,5个,1000个,看分配结果对不对。
2) 在vector_status中预设10个成员,全部无效:然后分配5个,看结果是否正确
3)在vector_status中预设10个成员,全部有效,然后分配1个,5个,9个,10个,11个, 1000个,看结果是否正确
这样就能发现那些判断是否正确,有没有考虑不周的情况。
两个测试行为,从项目经理,乃至同伴看来,行为完全一样,只是一个过脑子,一个不过脑子,测试效果完全不同。聚焦成功的团队才能实施单元测试,聚焦“我没错”的团队,做这种事情基本上是浪费时间。中间的情况是,测试执行工程师可能还没有掌握这种基本的技能,这就需要花时间来辅导了。
而代码Review应该解决什么问题呢。还是拿这个例子来说:从算法上来看一看,解决这样一个分配问题,尼玛需要两重循环吗?脑子生锈了吧?尼玛这也敢开始UT?信不信捏死你?——你看,这是代码Review干的活:)
补充说明:有人可能不知道我说的单元测试是什么,那么请参考:从单元测试理解软件。(所以,请注意,这里的测试的“单元”,是C文件,不是函数,你看到我的例子是一个函数,但如果这个函数调用了本C文件的另一个函数,那个函数是不打桩的)
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。