NSubstitute mock generic method
-
25-07-2021 - |
문제
I have the following method signature in an interface:
public interface ISettingsUtil
{
T GetConfig<T>(string setting, dynamic settings);
}
Which I have attempted to mock:
var settingsUtil = Substitute.For<ISettingsUtil>();
var maxImageSize = settingsUtil.GetConfig<long>("maxImageSize",
Arg.Any<dynamic>()).Returns(100L);
This throws a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException
exception on the 2nd line:
'long' does not contain a definition for 'Returns'
Any thoughts on how to mock T GetConfig<T>(string setting, dynamic settings)
correctly?
해결책
NSubstitute does not work with members that use dynamic
. (Github issue)
다른 팁
For anyone still struggling with this, you can actually mock dynamics in NSubsitute, it just requires jumping through some minor hoops. See the below case of mocking out calls to a signalR client hub.
The important line is this one:
SubstituteExtensions.Returns(_hubContext.Clients.All, _mockClient);
In order to mock the dynamic I have created an interface with the methods I want to listen for. You then need to use SubstituteExtensions.Returns rather than simply chaining a .Returns at the end of the object.
If you don't need to verify anything you could also use an anonymous object.
Full code sample follows:
[TestFixture]
public class FooHubFixture
{
private IConnectionManager _connectionManager;
private IHubContext _hubContext;
private IMockClient _mockClient;
[SetUp]
public void SetUp()
{
_hubContext = Substitute.For<IHubContext>();
_connectionManager = Substitute.For<IConnectionManager>();
_connectionManager.GetHubContext<FooHub>().Returns(_hubContext);
_mockClient = Substitute.For<IMockClient>();
SubstituteExtensions.Returns(_hubContext.Clients.All, _mockClient);
}
[Test]
public void PushFooUpdateToHub_CallsUpdateFooOnHubClients()
{
var fooDto = new FooDto();
var hub = new FooHub(_connectionManager);
hub.PushFooUpdateToHub(fooDto);
_mockClient.Received().updateFoo(fooDto);
}
public interface IMockClient
{
void updateFoo(object val);
}
}
public class FooHub : Hub
{
private readonly IConnectionManager _connectionManager;
public FooHub(IConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public void PushFooUpdateToHub(FooDto fooDto)
{
var context = _connectionManager.GetHubContext<FooHub>();
context.Clients.All.updateFoo(fooDto);
}
}