首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >单元测试和构造器依赖注入

单元测试和构造器依赖注入
EN

Stack Overflow用户
提问于 2012-10-15 21:20:11
回答 2查看 21.4K关注 0票数 7

我有一个关于如何设计一个适合单元测试的应用程序的问题。

我正在尝试实现SRP (Single-Responsibility Principle,单一责任原则),根据我的理解,这涉及到将大部分功能划分为独立的、专用的类,以保持代码更有条理。例如,我有这样一个特定的场景。

一个类RecurringProfile,它有一个方法.ActivateProfile()。此方法的作用是将状态标记为已激活,并为下一个到期日创建下一个(第一个)循环付款。我打算将功能拆分出来,在一个单独的类中创建下一个循环付款,例如RecurringProfileNextPaymentCreator。我的想法是让这个类将'RecurringProfile'作为其构造函数中的一个参数:

代码语言:javascript
复制
RecurringProfileNextPaymentCreator(IRecurringProfile profile)

然而,我认为这对于单元测试来说是有问题的。我想创建一个测试ActivateProfile功能的单元测试。这个方法将通过依赖注入( IRecurringProfileNextPaymentCreator )获得一个实例,并调用.CreateNextPayment方法。

我创建单元测试的想法是创建一个IRecurringProfileNextPaymentCreator的模拟,并替换它,这样我就可以验证.ActivateProfile()是否实际调用了该方法。但是,由于构造函数参数的原因,这不适合作为NInject的默认构造函数。仅仅为这种情况创建一个自定义的NInject提供程序(在这种情况下,我可以在整个解决方案中使用许多这样的类)可能有点夸大其词。

有什么想法/最佳实践可以帮助你做到这一点吗?

--以下是上述示例的示例代码:(请注意,代码是手写的,在语法上不是100%正确的)

代码语言:javascript
复制
public class RecurringProfile
{
    public void ActivateProfile()
    {
        this.Status = Enums.ProfileStatus.Activated;
        //now it should create the first recurring payment
        IRecurringProfileNextPaymentCreator creator = NInject.Get<IRecurringProfileNextPaymentCreator>();
        creator.CreateNextPayment(this); //this is what I'm having an issue about 
    }
}

和一个示例单元测试:

代码语言:javascript
复制
public void TestActivateProfile()
{   
    var mockPaymentCreator = new Mock<IRecurringProfileNextPaymentCreator>();
    NInject.Bind<IRecurringProfileNextPaymentCreator>(mockPaymentCreator.Object);

    RecurringProfile profile = new RecurringProfile();
    profile.ActivateProfile();
    Assert.That(profile.Status == Enums.ProfileStatus.Activated);
    mockPayment.Verify(x => x.CreateNextPayment(), Times.Once());

}

转到示例代码,我的问题是将RecurringProfile作为参数传递给creator.CreateNextPayment()方法是否是一种好的实践,或者在获得RecurringProfile的实例时以某种方式将IRecurringProfileNextPaymentCreator传递给DI-框架是否更有意义,因为IRecurringProfileNextPaymentCreator将始终作用于IRecurringProfile来创建下一次付款。希望这能让问题更清晰一些。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-10-16 19:53:56

因为您没有显示任何代码,所以我猜您想要这样做

代码语言:javascript
复制
public class RecurringProfile
{
  private readonly DateTime _dueDate;
  private readonly TimeSpan _interval;
  public RecurringProfile(DateTime dueDate, TimeSpan interval)
  {
    _dueDate = dueDate;
    _interval = interval;
  }
  public bool IsActive { get; private set; }
  public DateTime DueDate
  {
    get { return _dueDate; }
  }
  public TimeSpan Interval
  {
    get { return _interval; }
  }
  public RecurringProfile ActivateProfile()
  {
    this.IsActive = true;
    return new RecurringProfile(this.DueDate + this.Interval, this.Interval);
  }
}

这还不够简单吗?

更新

不要滥用DI容器作为ServiceLocator。您将支付创建者作为ctor参数注入的想法是正确的。ServiceLocator is considered an anti-pattern in modern application architecture。下面的代码应该可以很好地工作。

代码语言:javascript
复制
[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var mock = new Mock<INextPaymentCreator>();
    DateTime dt = DateTime.Now;
    var current = new RecurringProfile(mock.Object, dt, TimeSpan.FromDays(30));
    current.ActivateProfile();
    mock.Verify(c => c.CreateNextPayment(current), Times.Once());
  }
}
public class RecurringProfile
{
  private readonly INextPaymentCreator _creator;
  private readonly DateTime _dueDate;
  private readonly TimeSpan _interval;
  public RecurringProfile(INextPaymentCreator creator, DateTime dueDate, TimeSpan interval)
  {
    _creator = creator;
    _dueDate = dueDate;
    _interval = interval;
  }
  public bool IsActive { get; private set; }
  public DateTime DueDate
  {
    get { return _dueDate; }
  }
  public TimeSpan Interval
  {
    get { return _interval; }
  }
  public RecurringProfile ActivateProfile()
  {
    this.IsActive = true;
    var next = this._creator.CreateNextPayment(this);
    return next;
  }
}

public interface INextPaymentCreator
{
  RecurringProfile CreateNextPayment(RecurringProfile current);
}
票数 4
EN

Stack Overflow用户

发布于 2012-10-16 15:22:58

在这样的单元测试期间,你不应该使用DI容器(Ninject)。当新建被测类时,您将手动注入模拟对象。然后验证调用是在模拟上进行的。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12896480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档