过去两个月我一直在编写测试(在JavaScript中)。并且,我习惯检查模块是否具有某些属性。
例如:
// test/foo.js
const Foo = require('../lib/foo');
const Expect = require('chai').expect;
describe('Foo API', () => {
it('should have #do and #dont properties', () => {
Expect(foo).to.have.property('do')
.and.to.be.a('function');
Expect(foo).to.have.property('dont')
.and.to.be.a('function');
});
});
});
而且,我一直想知道我是否做了正确的事情。只是想知道一些事情:
这种模式“正确”吗?
如果不是“正确”?
它甚至有意义吗?
答案 0 :(得分:17)
不要测试类型。测试特定属性值是否符合预期值。
因此,编写一个调用foo并期望特定结果的测试,而不是“foo是一个函数”。
如果foo不是函数,则会生成错误并且测试将失败(这很好)。如果foo是一个函数,你将对该函数的行为进行适当的测试。
答案 1 :(得分:7)
有一个名为 duck typing 的范例,上面写着(from Wikipedia):
在鸭子打字中,程序员只关心确保这一点 对象在给定的上下文中表现为对它们的要求,而不是 确保他们属于特定班级。例如,在 非鸭类型语言,可以创建一个需要的函数 传递给它的对象是Duck类型,或者是类型的后代 鸭子,为了确保那个功能可以使用对象的 走路和嘎嘎的方法。在鸭式语言中,函数会 拿一个任何类型的对象,简单地调用它的walk和quack方法, 如果未定义运行时错误,则会产生运行时错误。代替 正式指定类型,鸭子打字实践依赖 文档,清晰的代码和测试,以确保正确使用。
我将重点关注以上文字的以下部分:
在鸭子类型的语言中,该函数将采用任何对象 键入并简单地调用其walk和quack方法,生成运行时 如果没有定义错误
和...
鸭子打字实践依赖于文档,清晰的代码和测试 确保正确使用。
也就是说,因为JavaScript是一种动态类型的语言非常适合鸭子打字。
换句话说,你应该避免这些测试。如果模块具有缺失属性或者存在不需要的类型,则会出现运行时错误,这足以说明调用者不符合隐式契约,无法正常工作给定模块。
在运行时代码行为定义的整个合同可以通过良好的文档页面强制执行。
答案 2 :(得分:1)
如果您编写测试,那是因为您希望确保进一步更改代码不会改变您正在测试的行为。
因此,如果您的模块将这些属性公开为接口,并且应用程序或其他应用程序上的其他代码依赖于它,那么它将非常有用。
但是,如果属性只是“内部”,我的意思是它只是依赖于模块实现,而不是危险和浪费时间,因为你应该总是能够改变实现。
接口不是这样。
在你的测试中,测试一个属性是否是一个函数,你应该测试函数是否做了调用时应该做的事情。
如果有文档证明此功能是模块的接口。