提问人:TSR 提问时间:5/14/2020 更新时间:7/27/2021 访问量:1702
我应该在单元测试中模拟每一个依赖项吗?
Should I mock every single dependencies in unit testing?
问:
经过大量阅读,我发现人们建议在单元测试中模拟方法中的每个依赖项。
但是,我有一个创建对象列表(依赖项)的方法,并使用这些对象的方法更改列表。在实际场景中,该方法是将客户端发送的数据有效负载转换为这些对象。 下面是 Typescript 中的代码实现,但它只是给你一个想法。
import {expect} from 'chai';
import 'mocha';
class Bar {
private val: number;
constructor(val: number) {
this.val = val;
}
isValid(): boolean {
return this.val !== 2;
}
canMergeWith(bar: Bar): boolean {
return this.val === bar.val;
}
}
class BarBuilder {
constructor() {
}
createBars(payload: number[]): Bar[] {
const bars: Bar[] = [];
payload.forEach(p => {
bars.push(new Bar(p));
});
const filtered = this.filterBars(bars);
const merged = this.mergeBars(filtered);
return merged;
}
private filterBars(bars: Bar[]): Bar[] {
return bars.filter(b => b.isValid());
}
private mergeBars(bars: Bar[]): Bar[] {
const merged: Bar[] = [];
bars.forEach((bar, i) => {
if (i > 0) {
const before = bars[i - 1];
if (!bar.canMergeWith(before)) {
merged.push(bar);
}
} else {
merged.push(bar);
}
});
return merged;
}
}
describe('BarBuilder', () => {
it('Should convert payload into valid bars', () => {
const payload = [1, 2, 3, 4, 4, 5, 5];
const barBuilder = new BarBuilder();
const bars = barBuilder.createBars(payload);
expect(bars).to.deep.equal([{val: 1}, {val: 3}, {val: 4}, {val: 5}]);
});
});
目前,测试正在通过。但这违反了我应该嘲笑类中每个依赖项的规则。
我的问题是我应该做以下哪一项:
- 我真的应该为了遵守该规则而嘲笑 Bar 类吗?
如果是这样,我发现这样做还有另一个困难。如果我模拟类 Bar,我如何制作模拟方法并根据它们的输入返回 true 或 false?这难道不等同于重写类本身吗?canMergeWith
isValid
- 我应该重构我的代码吗?
我也在想,问题可能出在我的代码不可测试上。之前,方法和属于.为了便于阅读,我把它们移到了 Bar 中。但现在,单元测试不再是竞争对手了。canMergeWith
isValid
BarBuilder
BarBuilder
- 尽管测试违反了单元测试规则,但仍保持原样。
答:
我发现人们建议在单元测试下模拟方法中的每个依赖项。
有些人建议这样做,是的。在链接的线程中,更多的人不同意。单元测试实际上是一个有争议的话题。
当我第一次得知单元测试的定义没有被整个行业广泛接受时,我感到很惊讶。人们经常谈论单元测试,好像只有一个定义,尤其是“模拟一切”的定义;但是,如果你通读链接的线程,你会看到这种方法的许多陷阱,尤其是这些测试的脆弱性。
我认为“模拟一切”的定义仍然如此普遍,因为它是多么微不足道。不涉及思考,只是嘲笑一切。另一种选择是考虑何时嘲笑,何时不嘲笑。那更难。你必须为嘲笑辩护。将单元测试定义为“模拟一切”更容易,因为这样你就不用考虑模拟的目的是什么。这只是定义的一部分。
Ian Cooper 做了一个关于单元测试的演讲,这是 Kent Beck 在 TDD 的背景下定义的。我发现他的观点比“模拟一切”的方法更现代。
Kent 以一种非常具体的方式使用单元测试。当他使用这个短语时,他的意思是你应该能够将所有测试作为一个套件一起运行,而不会运行一个测试影响其他测试。隔离的单位是测试。人们犯了很多错误,因为人们认为隔离的单位是被测试的类。事实并非如此。
我的建议是:当他们帮助你时,创造嘲笑。不要为了满足规则而创建模拟。这背后没有达成共识的规则。
评论
下一个:封装要求
评论