一个简单的问题:您如何区分功能,单元和集成测试?
有很多不同意见,但是我正在特别尝试确定如何组织涉及模型关系的Laravel测试。这是一些需要测试的PHP代码的示例:
public function prices()
{
return $this->hasMany(Prices::class);
}
public function getPriceAttribute($)
{
return $this->prices()->first() * 2;
}
据我所知,测试说明(可以纠正我):
单元测试
集成测试
功能测试
以下是我的问题:
因此,如果没有模型之间的模拟关系,我的测试将属于哪里?
答案 0 :(得分:2)
如果我正确地解释了您的原始问题,那么我认为这里的主要限制因素是:
因此,如果没有模型之间的模拟关系,我的测试将属于哪里?
如果不允许模拟,并且您需要触摸数据库,那么根据您/和Google的定义,它必须属于集成/中等大小测试:)
我认为获取价格属性功能的方式与DB分开。即使在模型中,价格也可能来自任何地方。现在,它是一个RDBMS,但是如果您的组织规模真的很大,又分裂成另一个服务怎么办?基本上,我相信getPriceAttributes
的功能不同于属性的存储:
public function getPriceAttribute($)
{
return $this->prices()->first() * 2;
}
如果您支持这种推理,它将创建支持单元测试的逻辑分隔。可以模拟prices()
来返回0、1和许多(2)结果的集合。该测试可以作为单元测试来执行(测试执行速度要快几个数量级(即与本地数据库通话的时间可能是1ms与 可能是10s或100s的数量级)
我不熟悉php测试生态系统,但是一种实现方法可能是使用test specific subclass(不确定以下内容是否有效的PHP:p):
class PricedModel extends YourModel {
function __construct($stub_prices_supporting_first) {
$this->stub_prices = $stub_prices_supporting_first;
}
public function prices() {
return $this->stub_prices;
}
}
测试
function test_priced_model_0_prices() {
p = new PricedModel(new Prices(array()));
assert.equal(null, p.getPriceAttribute());
}
function test_priced_model_1_price() {
p = new PricedModel(new Prices(array(1)));
assert.equal(2, p.getPriceAttribute());
}
function test_priced_model_2_prices() {
p = new PricedModel(new Prices(array(5, 1)));
assert.equal(10, p.getPriceAttribute());
}
希望以上内容可以让您完全控制getPriceAttribute
方法的输入,以支持直接的无IO单元测试。
- 同样,上面所有的单元测试都可以告诉您,您能够正确处理价格,如果您能够查询价格,则不会对价格产生任何反馈!
答案 1 :(得分:0)
测试的区别在于它们各自的目标:
单元测试旨在发现那些可以在软件的孤立小部分中发现的错误。 (请注意,这并不是说您必须 隔离-只是意味着您将重点放在隔离的代码上。通常不需要足够的隔离和嘲笑来实现此目标:请考虑调用{{ 1}}函数-您几乎不需要模拟它,只需让您的被测系统调用原始系统即可。)
集成测试旨在发现两个或多个组件交互中的错误,例如,对接口的相互误解。这些错误无法在隔离的软件中找到:如果隔离地测试代码,则也会基于对其他组件的理解(可能是错误的)编写测试。
您所描述的功能测试将以发现其他错误为目标,这些错误到目前为止是其他测试无法检测到的。此类错误的一个例子可能是,该功能的旧版本已集成(当时是正确的,但缺少某些功能)。
尽管可能令人惊讶,但结论是,在单元测试中进行数据库访问并不是严格意义上的禁止。请考虑以下情形:您开始编写单元测试并模拟数据库访问。后来,您意识到自己可以变得更懒惰,只使用数据库而不进行模拟-否则将所有测试保持原样。您的测试没有更改,它们将像以前一样继续在隔离的代码中发现错误。它们现在运行起来可能会慢一些,并且设置可能会比模拟数据库更复杂。但是,测试套件的目标是相同的-带有和不带有模拟数据库。
此方案稍微简化了事情,因为可能存在只能用模拟进行的测试用例:例如,测试数据库以特定方式损坏且代码可以正确处理的情况。使用真实的数据库,实际上不可能建立这样的测试用例。