使用遗留代码的Rhino Mocking和TDD

时间:2010-08-19 08:33:11

标签: unit-testing dependency-injection mocking

首先让我说我正在使用遗留代码。所以可以做出一些改变,但不能做出激烈的改变。

我的问题是我有一个“车辆”对象,它很简单,但没有接口或任何东西。这个项目是在TDD真正开始变得更加主流之前创建的。无论如何,我需要添加一种新方法来改变车辆的起步里程。我认为这将是尝试TDD和Mocking的良好开端,因为我是新手。我的问题是我需要创建一个车辆做一些涉及进入数据库的检查。对不起,如果我的问题不是100%明确,那就是我发帖的原因,因为我对Rhino Mocks适合的地方感到困惑(如果我需要的话)。

4 个答案:

答案 0 :(得分:2)

问题是依赖性。您的车辆类别取决于数据库。希望与数据库的所有交互都被封装到一个很好的类中,我们将在一秒钟内回复它。当您启动单元测试时,您希望能够在不必关心数据库的情况下测试车辆类。例如,您要检查您的SpeedUp(int x)方法是否确实将总速度提高了x。在这种方法中,它首先要求DB询问其当前速度。这意味着你必须有一个DB来测试!大坝,这听起来不是一个非常快速的测试,也不是可重复的。还有很多设置只是运行测试。

如果我们可以假装数据库,那会不会很棒?这就是模拟的用武之地。我们创建了一个包含所有数据库交互的类的模拟。然后我们设置模拟以响应预先编写的值。因此,例如当我们向DB询问当前速度时,您返回100。

所以现在当我们测试模拟返回100并且我们可以断言SpeedUp(int x)需要100并且向它添加x。

答案 1 :(得分:1)

Rhino模拟只能从接口或抽象类创建模拟,这些模拟不存在您的遗留代码。

TypeMock可以嘲笑任何东西,但不是免费的。

您可以使用Microsoft Moles来嘲笑这些。

但是,您应该考虑到Moles应该是您最后的解决方案,最好通过从业务层抽象数据层来重构代码并使其可测试。

HTH

答案 2 :(得分:0)

是否可以轻松创建Vehicle类型的实例(对象),然后调用您的方法进行测试?如果是,那么你很可能不需要模拟。

但是,如果您的Vehicle类型具有需要执行要测试的操作的依赖项(如数据库访问对象),那么您希望使用模拟数据库访问对象,该对象返回测试的固定值,因为您希望你的单元测试能够快速运行。

Vehicle  [depends On>] OwnerRepository [satisfied By] SQLOwnerRepository

所以你引入一个接口(一个OwnerRepository来获取所有者的详细信息,比方说)来分离两者之间的DB Interaction(定义合同)。让你真正的依赖(这里是SQLOwnerRepository)实现这个接口。还要设计代码,以便可以注入依赖关系,例如

public Vehicle (OwnerRepository ownerRepository) 
{  _ownerRepository = ownerRepository;  // cache in member variable }

现在在测试代码中,

Vehicle [depends On >] OwnerRepository [satisifed By] MockOwnerRepository 

您有框架,给定一个接口将创建它的模拟实现(请参阅Rhino / Moq框架)。因此,您不再需要实际的数据库连接来测试您的Vehicle类。 模拟用于抽象耗时/不可控制的依赖关系,以保持您的单元测试快速/可预测。

我建议您阅读“依赖注入”,以便更好地了解使用模拟的时间和原因。

答案 3 :(得分:0)

不能直接回答您的问题。但是值得看看以下内容。

Gabriel Schenker发布了关于在遗留系统中应用TDD的消息。 PTOM – Brownfield development – Making your dependencies explicit

本文介绍了如何使用dependencies explicit和使用依赖注入。它还告诉我Poor Man’s Dependency Injection。当只有默认构造函数时,这是必需的。

这样的东西
 public OrderService() : this(
    new OrderRepository(),
    new EmailSender(ConfigurationManager.AppSettings["SMTPServer"])
    )

本文还涉及为ConfigurationManager创建一个包装器,以使其可测试。