如何在 base class 方法中接收单元测试委托?
How to unit test delegate was received in base class method?
我目前有一个基本服务 class,我的所有服务都在扩展。这是其中一种方法的样子:
protected internal virtual T PerformServiceOperationWithExceptionHandling<T>(Func<T> func)
{
try
{
return func.Invoke();
}
...
}
在派生的 classes 中,我这样调用方法:
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();
简答 - 你不应该。单元测试是关于测试被测方法的行为,而不是实现细节。
长答案:
class 内部如何工作并不重要,只要它产生预期的结果即可。
您需要在最终 class 上测试您的 public 方法,看看它是否按预期工作。单独测试 base/abstract class 什么也证明不了。
我支持 Sunny Milenov 的回答,但会更进一步,建议您更改设计。我已经了解到,当您遵循组合而非继承的原则时,许多与测试基础 class 行为有关的头痛问题就会消失。
即,如果您将基础 class 重构为合作者,并将其注入到服务的构造函数中,则可以单独测试它并在服务的测试中对其进行模拟。无需担心测试抽象基础 class 或在所有服务测试中测试相同的异常处理。
您将在协作者的测试中测试协作者是否正确调用函数。
在服务的测试中,您可以立即将协作者模拟为 return 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);
}
我目前有一个基本服务 class,我的所有服务都在扩展。这是其中一种方法的样子:
protected internal virtual T PerformServiceOperationWithExceptionHandling<T>(Func<T> func)
{
try
{
return func.Invoke();
}
...
}
在派生的 classes 中,我这样调用方法:
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();
简答 - 你不应该。单元测试是关于测试被测方法的行为,而不是实现细节。
长答案: class 内部如何工作并不重要,只要它产生预期的结果即可。
您需要在最终 class 上测试您的 public 方法,看看它是否按预期工作。单独测试 base/abstract class 什么也证明不了。
我支持 Sunny Milenov 的回答,但会更进一步,建议您更改设计。我已经了解到,当您遵循组合而非继承的原则时,许多与测试基础 class 行为有关的头痛问题就会消失。
即,如果您将基础 class 重构为合作者,并将其注入到服务的构造函数中,则可以单独测试它并在服务的测试中对其进行模拟。无需担心测试抽象基础 class 或在所有服务测试中测试相同的异常处理。
您将在协作者的测试中测试协作者是否正确调用函数。
在服务的测试中,您可以立即将协作者模拟为 return 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);
}