3

我正在尝试对遗留项目进行测试。代码的编写方式通常是可测试的,但是一些第三方依赖项不是。我正试图围绕如何对看起来像这样的东西进行单元测试:

class InsightEmailJob : NHibernateJob
{
    public IInsightEmailService InsightEmailService { get; set; }
    public IReportService ReportService { get; set; }
    public ITemplatedNotifier TemplatedNotifier { get; set; }
    public string ReplyEmail { get; set; }
    public string ReplyName { get; set; }

    public InsightEmailJob(ISession session,
        ILog log,
        IInsightEmailService insightEmailService,
        IReportService reportService,
        ITemplatedNotifier templatedNotifier,
        SystemReplyEmailSpec systemReplyEmailSpec)
        : base(session, log)
    {
        InsightEmailService = insightEmailService;
        ReportService = reportService;
        TemplatedNotifier = templatedNotifier;
        ReplyEmail = systemReplyEmailSpec.ReplyEmail;
        ReplyName = systemReplyEmailSpec.ReplyName;
    }

    public int AccountID{ get; set; }
    private Account mAccount;

    public Account Account
    {
        get
        {
            if (this.mAccount == null)
            {
                mAccount = this.InsightEmailService.Get<Account>(AccountID);
            }
            return mAccount;
        }
    }


    protected override void DoWork(JobExecutionContext context)
    {
        var insightEmail = InsightEmailService.FindAndIncrementEmailForAccount(Account);
        var report = ReportService.LoadMultiReportByName(insightEmail.ReportName);
        var reportData = ReportService.Execute(report, new ParameterValuesDictionary(Account, DateTime.Now.AddDays(-7), DateTime.Now, 0));
        var templateData = new Hashtable {{"data", reportData}, {"account", Account}};
        foreach (var u in Account.Users.Where(x => x.Notify))
        {
            TemplatedNotifier.Send(u.UserName, ReplyName, ReplyEmail, insightEmail.TemplateName, templateData);
        }
    }
}

我知道很多人会建议使用 Mocks 或 Stubs 来传递而不是接口,但是对于这实际上是如何有益的,我有点困惑。似乎这只会确保调用适当的方法,这让我觉得有些空洞,而且与作业的实施相结合,无法成为真正有效的测试。最终问题变成了,您如何对不返回任何值并且实际上只会导致副作用的东西进行单元测试,而不只是测试它是否按照您所说的方式实现?

4

2 回答 2

1

编写单元测试是为了证明您的实现工作正常。如果您的测试代码变得过于复杂并且事情变得越来越难以模拟,那么实现代码也可能变得越来越复杂且难以理解。

当您决定在模拟依赖项方面有太多工作时,您应该重新考虑您的设计并旨在将其重构为更简单的形式。

只需查看您的构造函数,我们就可以看到它可能做了太多工作。您有 6 个依赖项,您必须模拟所有依赖项才能编写有效的单元测试。我认为您在这里没有足够的抽象,因为您必须处理 NHibernate 会话、一些报告服务并发送电子邮件。

存储库模式是一种用于抽象数据访问代码的常见模式。您还应该将电子邮件发送部分移至另一个类并在此处使用其接口。

模拟没有返回值的方法非常容易。通过模拟这些方法调用,您可以证明您的类正确地使用了外部依赖项。您可以为参数值编写断言以及调用它的次数来验证您的代码。

无论如何,这是一个如何在Moq中模拟方法的示例

insightEmailService.Setup(mock => mock.FindAndIncrementEmailForAccount(It.IsAny<Account>()))
                   .Verifiable();
于 2013-05-01T18:28:16.440 回答
0

当您进行单元测试时,您只是在测试单元。这意味着在给定的外部依赖项下,单位如何(例如从其他服务调用方法的方法)。因此,如果您的测试代码行为正确,您需要查看外部依赖项的各种情况。

对于不返回任何内容的方法,有多种方法可以验证这一点

  1. 如果您使用 Mocking 框架,例如 Moq,您可以使用验证来确保使用适当的参数调用外部方法。
  2. 您可以使用回调验证传递给外部方法的内容(moq 具有良好的回调机制)
于 2013-05-01T18:26:25.660 回答