如何模拟.net中的函数委托



我在控制器中有一个Func<>属性,我想知道如何模拟它。

下面是我的代码:
public Func<HttpRequest, string> GetId { get; set; }

在控制器的构造函数中的用法如下:

GetId = req => req.HttpContext.Items["random-id"].ToString();

,下面是在各个方法和API调用中的用法:

var id = GetId (Request);

上述Func<>属性用于控制器及其构造函数中的所有API调用。我试图模拟它,这样我就不必在编写单元测试时处理它。有什么建议吗?

感谢@Nkosi在评论中给出的原始解决方案。

因为你的委托是在控制器的构造函数中初始化的,你必须首先构造控制器,然后在构造后分配一个委托,这是因为GetId有一个公共setter。

那么你的测试结果看起来就像

[Test]
public void SomethingTest()
{
// arrange
var controller = new YourController(...);
controller.GetId = req => "SomeFakeId";
// act
var someResult = controller.SomeMethod(...);
...
}

可能重构

将setter设为私有,以防止开发人员在生产代码中将委托更改为可能不合适的东西,这是可能的,而且可能是一个好主意。在这种情况下,你可以将setter设置为private(事实上,如果委托不打算在控制器外部被访问,你可以将整个属性设置为私有)。

public Func<HttpRequest, string> GetId { get; private set; }

private Func<HttpRequest, string> GetId { get; set; }

但是,如果这样做,上面的测试将无法编译。要解决这个问题,您可以通过反射访问setter,但这会使测试变得脆弱。

一个更好的解决方案是在构造函数中添加一个依赖项,并删除Func<>属性。让我们称这个依赖为IIdProvider。生产代码看起来就像

public interface IIdProvider
{
string GetId(HttpRequest request);
}
public class ContextItemsIdProvider : IIdProvider
{
public string GetId(HttpRequest request)
=> request.HttpContext.Items["random-id"].ToString()
}
public class YourController: ...
{
public IdProvider { get; private set; }
public YourController(..., IIdProvider idProvider)
{
IdProvider = idProvider;
}
public IActionResult SomeMethod()
{
var id = IdProvider.GetId(Request);
...
}
}

现在,您可以修改您的测试来创建控制器,从一个mock框架或您创建的存根传入一个真正的mock。下面是一个使用存根的例子:

[TestFixture]
public class YourControllerTests
{
private class IdProviderStub : IIdProvider
{
public string GetId(HttpRequest request)
=> "SomeFakeId";
}
[Test]
public void SomethingTest()
{
// arrange
var controller = new YourController(..., new IdProviderStub());

// act
var someResult = controller.SomeMethod(...);
...
}
}

我能够在设置函数中将模拟函数分配给控制器的属性。像这样:

[SetUp]
public void Setup()
{
var funcMock = new Mock<Func<HttpRequest, string>>();
_testController= new TestController();
_testController.GetId  = funcMock.Object;
}

最新更新