将依赖项注入域模型

时间:2013-12-20 19:57:35

标签: c# unit-testing dependency-injection mocking

我的域模型有一个OccurredOn字段,应该初始化为当前日期。通常我会在构造函数中将它设置为DateTime.Now。

现在我正在尝试创建一个IClock接口,以便我可以模拟DateTime.Now进行单元测试。但是,现在我会将IClock依赖项注入我的域模型吗?我应该手动将实例化的Clock实例传入构造函数吗?我应该使用我的DI容器来解决构造函数中的依赖关系吗?我应该创建一个工厂类来给我一个初始化对象吗?我离开基地了吗?

为此:

public class LogItem : KeyedEntityBase
{
    public LogItem(string message, string description, string status)
        : this()
    {
        Message = message;
        Description = description;
        Status = status;
    }
    private LogItem() { }

    public DateTime OccurredOn { get; set; }
    public string Message { get; set; }
    public string Description { get; set; }
    public string Status { get; set; }
}

此:

var newItem = new LogItem("message", "desc", "status", new Clock());

或者这个:

var newItem = LogItemFactory.CreateNewLogItem("message", "desc", "status");

或者这个:

public LogItem(string message, string description, string status)
    : this()
{
    Message = message;
    Description = description;
    Status = status;

    var clock = ObjectFactory.GetInstance<IClock>();
    OccurredOn = clock.Now();
}

2 个答案:

答案 0 :(得分:1)

最简单的方法是在LogItem中声明一个只有测试可用的附加c'tor,如下所示:

public class LogItem : KeyedEntityBase
{
    public LogItem(string message, string description, string status)
        : this()
    {
        Message = message;
        Description = description;
        Status = status;
    }
    private LogItem() { }

    internal LogItem(string message, string description, string status, DateTime dateTme)
        : this(message, description, status)
    {
        OccuredOn = dateTime;
    }

    public DateTime OccurredOn { get; set; }
    public string Message { get; set; }
    public string Description { get; set; }
    public string Status { get; set; }
}

然后使用InternalVisibleTo属性授予您的测试程序集对内部的独占访问权。

然后在你的测试中(只有那里)你可以写:

var newItem = new LogItem("message", "desc", "status", <date-whatever>);

我很清楚很多人会说你不应该为你的域模型添加代码只是为了让它可以测试,但是我个人并不认为这个论点太严肃了,但实践建议不然。

答案 1 :(得分:0)

尝试这种方式:

//your object
public class LogItem {
   public LogItem(string message, string description, string status) {
      Message = message;
      Description = description;
      Status = status;
   }
   public System.DateTime OccurredOn { get; set; }
   public string Message { get; set; }
   public string Description { get; set; }
   public string Status { get; set; }
}

public interface ILogFactory {
   LogItem CreateNew(string message, string description, string status);
}

public class LogFactory: ILogFactory {
   private readonly IClock clock;
   public LogFactory(IClock clock) {
      Requires.IsNotNull(clock, "clock");
      this.clock = clock;
   }
   public LogItem CreateNew(string message, string description, string status) {
      var item = new LogItem (message, description, status);
      item.OccurredOn = clock.Now();
      return item;
   }
}

public interface IClock {
   System.DateTime Now();
}

为了测试:

using NSubstitute; // see http://nsubstitute.github.io/
using NUF = NUnit.Framework;
[NUF.TestFixture]
public class LogFactoryTest {
   [NUF.Test]public void CreateNew_should_call_now() {
      var clock = Substitute.For<IClock>();
      var fac = new LogFactory(clock);
      fac.CreateNew("a", "b", "c");
      clock.Received().Now();
   }

   [NUF.Test]public void CreateNew_should_set_correct_value() {
      var clock = Substitute.For<IClock>();
      var now = new System.DateTime(2014, 1, 15, 18, 0, 0);
      clock.Now().Returns(now);

      var fac = new LogFactory(clock);
      LogItem log = fac.CreateNew("a", "b", "c");

      NUF.Assert.That(log.OccurredOn, NUF.Is.EqualTo(now));
   }
}