我知道在单元测试中,首选硬编码,因为我们不想写太多的代码。
但是,对于集成测试,是否仍然应用相同的原理?对于上下文,这是一个常见的场景:
以下是有关两种方法的虚构代码:
// Arrange
$note = new Note('note123', 'John', 'example message');
$this->noteRepository->save($note);
// Act
$response = $this->json('GET', '/api/notes');
// Assert
$response->seeJsonEquals([
'id' => 'note123',
'author' => 'John',
'message' => 'example message'
]);
// Arrange
$note = new Note('note123', 'John', 'example message');
$this->noteRepository->save($note);
// Act
$response = $this->json('GET', '/api/notes');
// Assert
$noteSerializer = new NoteSerializer();
$response->seeJsonEquals($noteSerializer->serialize($note));
软编码方法的唯一问题是,如果串行器出了问题,则测试仍然会通过,因为控制器和期望值都使用了它。
但是,我们可以通过为序列化程序创建另一个测试来解决这个问题。
我们可能有很多迷你测试,但与硬编码相比,我认为它可以节省很多时间。如果我们更改了响应结构,那么硬编码的测试也将需要进行一些更改,而软编码的测试仅需要在自己的测试中进行更改。
我可能会遗漏一些东西,但是我已经尝试过搜索它,而我所看到的总是关于单元测试的,所以想问一问同样的原理是否仍适用于集成测试
答案 0 :(得分:2)
TL; DR:是的,拥有集成测试并假设其他测试策略都承担着发现错误的责任是很有意义的。
我想您会发现这里有两种不同的想法,它们混杂在一起。
一个问题是独立验证。您可以运行许多测试来证明给定解决方案在内部是一致的,但这并不等于证明给定解决方案是正确的。后者通常需要查询测试对象以获取数据,然后进行独立评估。
UltimateAnswer lifeTheUniverseAndEverything = deepThought()
// Compare this
assertEquals(new UltimateAnswer(42), lifeTheUniverseAndEverything);
// to this
assertEquals(42, lifeTheUniverseAndEverything.toInt());
什么才算独立?我认为这是一条模糊的界线-如果我们有足够的测试来对UltimateAnswer :: equals具有任意数量的9信心,那么将验证视为独立是可以的。另一方面,通过使用域不可知的原语“独立地”验证事情是否正常,我至少被烧了两次,结果发现我实际上是在进行依赖验证,而测试失败了捕获我期望的错误。
第二个问题是过度拟合-通常,许多可区分的行为都可能令人满意。示例:List.shuffle()
的结果应该是什么?如果测试旨在描述您的要求,那么与记录示例行为的测试相比,它们将更宽容。
当您重构主要活动时,严格安装的测试非常棒,并且您试图验证所做的更改确实保留了系统的准确行为。当测试一个新的系统时,其行为的核心偏差很小,到处都会出现,它们可能会很糟糕(考虑更改日期格式要求后,考虑对输出字符串进行验证的测试)。
在我看来,“集成测试”与“单元测试”的这些关注点都没有什么特别的区别。诚然,问题的一部分在于,它从来没有特别清楚另一个人从这些想法的定义开始。
在大多数情况下,不同种类的测试会有不同的权衡。我们希望我们的验证具有成本效益。因此,我们可能将要有一个分层的测试策略,其中我们执行的检查类型取决于上下文。
答案 1 :(得分:0)
在您的特定示例中,使用NoteSerializer
没有意义,因为您的断言使用与实现相同的代码来构建。
您认为此测试有价值吗?
// Arrange
$original = 42;
$expected = $original + 100;
// Act
$actual = $original + 100;
// Assert
this-> assertEquals($expected, $actual);
使用NoteSerializer
来构建期望值的问题是,正如您已经注意到的,如果序列化程序损坏,测试将保持绿色。
相反,您可以将收到的响应反序列化为一个类,然后将其与原始$note
进行比较