如果功能的副作用是设计中固有的,我该如何开发这样的功能?
例如,如果我想实现一个类似 http.get("url") 的函数,并且我通过依赖注入(inject)将副作用作为服务 stub ,它看起来像:
var http = {
"get": function( url, service ) {
return promise(function( resolve ) {
service( url ).then(function( Response ) {
resolve( Response );
});
});
}
}
...但是我需要实现与原始 http.get(url) 相同的服务,因此会产生相同的副作用,因此将我置于开发循环中。我是否必须模拟服务器来测试这样的功能?如果需要,它属于 TDD 开发周期的哪一部分?是集成测试,还是单元测试?
另一个例子是数据库模型。如果我正在开发与数据库一起使用的代码,我将设计一个接口(interface),抽象一个实现该接口(interface)的模型,然后使用依赖注入(inject)将其传递到我的代码中。只要我的模型实现了接口(interface),我就可以使用任何数据库并轻松地 stub 它的状态和响应,以便为与数据库交互的其他功能实现 TDD。但是那个模型呢?它将与数据库交互——似乎这种副作用是设计中固有的,当我去实现那个抽象时,把它抽象掉会让我进入一个开发循环。我如何实现模型的方法而不能够将它们抽象出来?
最佳答案
In the TDD how do you write tests for code that inherently have side effects?
我认为我在任何地方都没有看到特别明确的答案。最接近的可能是 GOOS ——TDD 的“伦敦”学派倾向于关注外部。
但从广义上讲,您需要了解副作用属于 imperative shell。 .它们通常在基础设施组件中实现。因此,您通常需要更高级别的抽象,您可以将其传递给系统的功能部分。
例如,读取系统时钟是一个副作用,会产生一个自纪元以来的时间值。您的大多数系统不应该关心时间来自哪里,因此读取时钟的抽象应该是系统的输入。
现在,感觉就像是“一路乌龟”——你如何测试你与基础设施的交互? Kent Beck describes停止条件
I get paid for code that works, not for tests, so my philosophy is to test as little as possible to reach a given level of confidence....
我倾向于依靠Hoare's observation
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies
一旦你着手实现一个明显正确的副作用,你就不再担心它了。
当你盯着一个副作用,并且实现显然不正确时,你开始寻找将硬部分拉回功能核心的方法,进一步隔离副作用。
副作用的实际测试通常发生在您开始将所有组件连接在一起时。由于副作用,这些测试通常较慢;因为它们共享可变状态,您通常需要确保它们按顺序运行。
关于tdd - 在 TDD 中,您如何为本来就有副作用的代码编写测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49418491/