【问题标题】:How to unit test delegate was received in base class method?如何在基类方法中收到单元测试委托?
【发布时间】:2015-05-10 05:38:35
【问题描述】:

我目前有一个基础服务类,我的所有服务都对其进行了扩展。这是其中一种方法的样子:

   protected internal virtual T PerformServiceOperationWithExceptionHandling<T>(Func<T> func)
        {
            try
            {
                return func.Invoke();
            }

            ...
        }

在派生类中我这样调用方法:

    public AddGuestResponse AddGuest(AddGuestRequest addGuestRequest)
    {
        return PerformServiceOperationWithExceptionHandling(() => AddGuestLogic(addGuestRequest));
    }

我想测试 AddGuest 并确保“AddGuestLogic”作为基本方法中的参数传递?如何使用 nSubstitute 和 nUnit 实现这一点。我觉得不可能?

================================================ =

我最终使用了以下代码:

    [Test]
    public void AddGuest_WhenCalled_PerformsAddGuestLogicWithExceptionHandling()
    {
        Func<AddGuestResponse> addGuestLogic = null;
        _guestService.PerformServiceOperationWithExceptionHandling(Arg.Do<Func<AddGuestResponse>>(arg => addGuestLogic = arg));
        var addGuestRequest = new AddGuestRequest();
        _guestService.AddGuest(addGuestRequest);
        _guestService.ClearReceivedCalls();

        addGuestLogic.Invoke();

        _guestService.Received().AddGuestLogic(addGuestRequest);
    }

_guestService 在我的设置方法中创建如下: Substitute.ForPartsOf();

【问题讨论】:

    标签: c# unit-testing delegates nunit nsubstitute


    【解决方案1】:

    简短的回答 - 你不应该。单元测试是关于测试被测方法的行为,而不是实现细节。

    长答案: 类的内部工作方式无关紧要,只要它产生预期的结果即可。

    您需要在最终类上测试您的公共方法,看看它是否按预期工作。孤立地测试基类/抽象类并不能证明任何事情。

    【讨论】:

      【解决方案2】:

      我赞同 Sunny Milenov 的回答,但会更进一步,建议您更改设计。我了解到,当您遵循组合优于继承的原则时,许多与测试基类行为有关的令人头疼的问题都会消失。

      即,如果您将基类重构为协作者,并将其注入服务的构造函数中,则可以单独对其进行测试并在服务的测试中对其进行模拟。无需担心在所有服务的测试中测试抽象基类或测试相同的异常处理。

      您将测试协作者是否正确调用了协作者测试中的函数。

      在服务的测试中,您可以模拟协作者立即返回 Func 的结果:

       [Test]
       public void ServiceLogicIsExecuted()
       {
           var collaborator = Substitute.For<ICollaborator>();
      
           //Tell the test double to return the Func's result. You'd probably want to do this in the setup method.
           collaborator.PerformServiceOperation(Arg.Any<Func<int>>()).Returns(x => ((Func<int>)x[0]).Invoke());
      
           var sut = new Service(collaborator);
      
           var result = sut.CalculateSomething();
      
           Assert.That(result, Is.EqualTo(99));
       }
      
       public class Service
       {
           private readonly ICollaborator _collaborator;
      
           public Service(ICollaborator collaborator)
           {
               _collaborator = collaborator;
           }
      
           public int CalculateSomething()
           {
               return _collaborator.PerformServiceOperation(ExecuteLogic);
           }
      
           private static int ExecuteLogic()
           {
               return 99;
           }
       }
      
       public interface ICollaborator
       {
           T PerformServiceOperation<T>(Func<T> func);
       }
      

      【讨论】:

      • 我遇到的问题是我无法测试我的基类方法是否收到了正确的 lambda 表达式。即使我要改变它以支持组合而不是继承(我确实考虑过)我仍然有同样的问题,即因为两个 lambda 表达式即使是相同的也会指向两个不同的内存位置。
      • 如果我对 Sunny 的理解正确,我也同意他/她的观点......即,支持基于状态的测试而不是基于交互的测试?虽然我觉得这样做是在我的所有服务测试中测试相同的异常处理。
      • 我为服务测试添加了一个示例,展示了如何确保您的合作者测试双重执行服务的逻辑,该逻辑作为 Func 传递给它。我不认为 Sunny 打算诋毁基于交互的测试。他的观点是,孤立地测试基类没有什么用,因为基类的行为将始终与派生类的逻辑交互执行。
      • 感谢您提供代码示例 :) 尽管您的解决方案更好,但我最终还是坚持使用继承。我实现了我在博客文章中概述的目标:journeymanyears.wordpress.com/2011/12/10/…
      • 没问题!另一种方法可能是在您的基类中添加一个测试挂钩,即 Func 的 getter,您仅将其用于测试。我认为这是一个不错的折衷方案,但很多人强烈反对将这种挂钩添加到生产代码中。
      猜你喜欢
      • 1970-01-01
      • 2013-08-24
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 2022-07-02
      • 1970-01-01
      • 2014-04-09
      相关资源
      最近更新 更多