2

我正在编写一个测试,我必须将一个模拟传递给一个特定的方法。我想知道,通过构造函数或直接将其传递给相关方法有什么好处。或者这并不重要。

例如

通过构造函数传递接口/模拟

class User()
{
  IClock clock;

  User(IClock clock) {this.clock = clock;}

  User GetUser(){ ..}
  UpdateUser(User user) {
     ... 
     this.clock.Now();
     ...
  }
}

对比

将接口/模拟传递给方法

class User()
{
  User GetUser(){ ..}
  UpdateUser(IClock clock, User user) {
     ... 
     clock.Now();
     ...
  }
}

谢谢!

编辑

在这种情况下,IClock 将包装 DateTime。我这样做是为了可测试性。如此有效,我将有一个覆盖,它将在其内部构造 IClock。

例如。对于方法案例:

UpdateUser(User user) {
  UpdateUser(new Clock(), user);
}

Clock 将封装 DateTime。

4

3 回答 3

3

在这种情况下,IClock 是一个隐藏的单例 - 它从底层操作系统生成当前日期时间。如果您想编写可靠的验收测试,您将需要在这些测试中剔除日期时间提供程序。

因此,您需要将这种依赖关系一直暴露到模块边界,然后将公共日期时间源注入您的模块(甚至可能是整个系统)。在编写验收测试时,您有时会存根此“IClock”日期时间源。

这将允许您回答以下问题:

  • 我的系统在闰日工作吗?
  • 我的系统在午夜工作吗?
  • 我的系统在除夕夜工作吗?

在构造函数签名或方法签名中具有 IClock 依赖项将向用户清楚地记录您的方法或对象是日期时间相关的。在尝试理解对象的行为时,对日期时间的依赖通常至关重要,因此应予以重视。通常避免任何隐藏这种依赖关系的东西(例如在重载的方法定义中提供默认的日期时间源),因为这会阻止您编写像我上面描述的那样的验收测试。

在将依赖项传递给构造函数或方法之间进行选择通常取决于方法对系统其余部分的责任。这些方法形成了对象将用于与运行系统中的对等方通信的公共协议,而构造函数参数是此特定实现执行其角色所需的依赖项。

问自己以下问题:

  • 这种方法的责任是否必然需要了解日期时间?
  • 还是与此特定对象中方法的特定实现有关,这意味着它需要日期时间?
  • 方法的调用者负责操作该对象的日期时间视图是否合理,或者这是模块配置的责任?
  • 如果我将此对象与同一接口的另一个实现交换,那么该实现不需要此方法的日期时间是否合理?
  • 您的对象中有多少方法需要日期时间?他们需要相同的日期时间视图吗?
于 2012-07-19T10:27:25.873 回答
1

这取决于谁知道IClock:它在User构造的地方可用还是在UpdateUser被调用的地方可用?

如果将它传递给构造函数,则可以传递User对象并且任何人都可以调用UpdateUser,但如果将其传递给UpdateUser,则使用该方法的任何人都必须持有IClock引用。

于 2012-07-19T09:19:09.707 回答
1

将依赖项(即IClock)传递给构造函数,即构造函数注入通常意味着这是给定类的整个对象所需的依赖项。将依赖传递给类的方法,即方法注入,只使这个特定的方法依赖于它。还有其他两种可能性:向属性注入依赖项(属性注入,即类对象的可选依赖项)和很少使用的环境上下文。

所以这更多的是你团队中代码风格的品味、设计和一致性问题。

在您的示例IClock中,对象不需要依赖User,因此方法注入更合适。

于 2012-07-19T09:28:46.260 回答