我一直被告知单元测试应该是独立于实现的。这对我来说非常有意义 - 如果函数的实现发生变化,它仍然应该满足其API契约,因此不需要更改单元测试。
然而,这似乎与嘲弄的想法相冲突。如果我需要模拟外部依赖项,这会立即使我的测试依赖于我的实现。如果我要使用不同的依赖项,我必须在单元测试中更改模拟代码。将所有单元测试完全独立实现是不切实际/不合理的吗?
答案 0 :(得分:0)
我不确定我是否理解你,但我会尽力而为。
假设您为课程PrepaidPointManager
编写单元测试。它有方法void Recharge(int points)
为用户点帐户充值。在这个课程的实施过程中你决定它应该取决于某些ICreditCardProvider
(因为你想换取现金换取积分),还取决于IPointRepository
,因为你想在一些存储中保持新的积分余额。 / p>
充值方法在使用信用卡提供商之前和存储余额之前进行一些计算。
CreditCardProvider
的任何特定实现拖到测试中(可能因为它需要Internet连接并且速度很慢)。同样的故事是PointRepository
。对于这些依赖项,您可以创建存根/模拟(ICreditCardProvider
和IPointRepository
),然后将它们注入PrepaidPointManager
。
[Test]
public void ShouldCallCreditCardProviderPoints() {
// arrange
ICreditCardProvider creditCardProvideMock = mock<ICreditCardProvider>();
IPointRepository pointRepositoryStub = stub<IPointRepository>();
PrepaidPointManager sut = new PrepaidPointManager(creditCardProvideMock ,pointRepositoryStub );
// act
sut.Recharge(100);
// asserts/verify
creditCardProvideMock.Received().Charge(/*...*.) // verify if charge was called
}
现在,您的测试独立来自依赖项的实现细节(ICreditCardProvider,IPointRepository)。这就是我从你的问题中理解术语独立实现的方式。
当然,如果您认为PrepaidPointManager应该具有其他依赖关系,那么单元测试代码也需要更改。
在设计良好的系统中,您可以编写针对抽象的代码,当您确实需要更改某些类的依赖关系时,此更改将仅影响少量测试。
嘲笑背后的主要思想(从我的角度来看)是当你编写测试用例时,你会说出这样的话:
我只是想测试一下,当我充值时,使用信用卡提供商。
如果您想要具体的CreditCardProvider的测试实现,那么只需为此目的创建一组新的测试。
使用mock实际上非常实用。如果您愿意,可以从本书Art of unit testing
中学到一些东西