所以有一段时间我现在一直在思考真正的单元测试应该包含什么。最好用一个例子来证明这一点,这是我想测试的角度服务:
function stringUtils() {
return {
splitFilterString: splitFilterString
}
function splitFilterString (filterString) {
return filterString.split(':');
}
}
我一直在想我的两种方法中哪一种最能描述一种真实的" splitFilterString
的单元测试。 (这些例子是用茉莉花写的)
String.prototype.split
已被调用。你不测试任何真实的"这样的示例,您只需测试该函数实际调用String.prototype.split
并返回该函数返回的内容。到目前为止,这一直是我的测试方式,因为你不测试"外部" API用于执行他们(应该)正在做的事情。
it('should call split with ":" as an argument and return whatever split returns', function () {
var filterString = 'foo:bar';
spyOn(String.prototype, 'split').and.returnValue('foo');
expect(stringUtils.splitFilterString(filterString)).toBe('foo');
expect(String.prototype.split).toHaveBeenCalledWith(':');
});
我也喜欢这种方法,因为您测试实际输入/输出如何使用该函数。在缺点方面,你也在这里间接测试String.prototype.split
,你正在测试它是否真的按照它所说的那样进行测试。
it('should return the correct output', function () {
expect(stringUtils.splitFilterString('foo:bar')).toEqual(['foo', 'bar']);
expect(stringUtils.splitFilterString('foobar')).toEqual(['foobar']);
});
答案 0 :(得分:2)
在第一个例子中,您将测试与实现相结合,这是主要区别。
如果您发现要使用superFastSplit
,那么这可能会妨碍您,因为您需要更改代码和您的测试。
所以在这种情况下,测试并没有提供真正的价值,因为当你想要重构时它会阻止你:"我想重构那部分代码,但那些该死的测试意味着我必须做所有的事情两次强>"!
在第二个示例中,您正在测试行为,因此您的代码将允许您使用superFastSplit
,因为您对如何获得结果不感兴趣,您感兴趣的是结果在实现中是一致的。< / p>
编辑 OP评论后
在外部API的情况下,我仍然会遵循“不要伤害我未来的自我/同事”#34;路径,所以我会做最简单的事情,IMHO是使用模拟外部API的模块,如下所示:
nock('http://external.api.com')
.get('/end/point')
.reply(200);
当然,你必须要小心并且不要尝试覆盖太多的场景,因为你基本上决定外部API会返回什么,所以我想我会在这里测试ok和nok场景,并涵盖集成测试中的所有细节。
nock所涵盖的内容是:
Nock通过覆盖Node的http.request函数来工作。此外,它也会覆盖http.ClientRequest,以覆盖直接使用它的模块。
您可以随时测试API调用在代码中的任何副作用,但我觉得在应用TDD时更难以遵循这种方法。