Question

I have web app which interact to WCF service with task-based async functions. When i was writing a unit tests i encountered with a problem about mocking async functions calls. I think no need to show unnecessary business logic because all fails only on these async calls. I've tried to do this in two ways, the first was:

[TestMethod]
public async Task GetAndSentSentinelList_AddedTest()
{
    var mockRemoteClient = new Mock<IRemoteManager>();
    mockRemoteClient.Setup(method => method.GetSentinelListAsync(new GetSentinelListRequest()))
        .Returns(Task.FromResult(new GetSentinelListResponse(new RemoteManager.SentinelItem[4]
        {
            new RemoteManager.Item() {Id = "Identity_Host"},
            new RemoteManager.Item() {Id = "Nose_Host"},
            new RemoteManager.Item() {Id = "Sunny_Host"},
            new RemoteManager.Item() {Id = "Ups_Supsu"}
        })));


    var rep = new Repository(hub)
    {
        Client = mockRemoteClient.Object,
        Sentinels = new List<SentinelItem>()
        {
            new Item() {Id = "Identity_Host"},
            new Item() {Id = "Nose_Host"},
            new Item() {Id = "Sunny_Host"}
        }
    };

    var res = await rep.Client.GetSentinelListAsync(new GetSentinelListRequest());

    //Assert
    Assert.IsNotNull(res );

}

The next was:

    [TestMethod]
    public async Task GetAndSentSentinelList_AddedTest()
    {
            //Arrange

            var tcs = new TaskCompletionSource<GetSentinelListResponse>();

            var expectedResult = new GetSentinelListResponse(new RemoteManager.SentinelItem[4]
                {
                    new RemoteManager.Item() {Id = "Identity_Host"},
                    new RemoteManager.Item() {Id = "Nose_Host"},
                    new RemoteManager.Item() {Id = "Sunny_Host"},
                    new RemoteManager.Item() {Id = "Ups_Supsu"}
                });

            tcs.SetResult(expectedResult);

            var mockRemoteClient = new Mock<IRemoteManager>();
            mockRemoteClient.Setup(method => method.GetSentinelListAsync(new GetSentinelListRequest()))
                .Returns(tcs.Task);


            var rep = new Repository(hub)
            {
                Client = mockRemoteClient.Object,
                Sentinels = new List<SentinelItem>()
                {
                    new Item() {Id = "Identity_Host"},
                    new Item() {Id = "Nose_Host"},
                    new Item() {Id = "Sunny_Host"}
                }
            };

            var res = await rep.Client.GetSentinelListAsync(new GetSentinelListRequest());

            Assert.IsNotNull();
    }

Perhaps i'll add generated code, cause at now i confused why it isn't working properly, generated abstract interface IRemoteManager:

 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
 [System.ServiceModel.ServiceContractAttribute(ConfigurationName="RemoteManager.IRemoteManager", CallbackContract=typeof(Web_Manager.RemoteManager.IRemoteManagerCallback))]
public interface IRemoteManager 
{
    [System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/IRemoteManager/GetSentinelList", ReplyAction="http://tempuri.org/IRemoteManager/GetSentinelListResponse")]
    System.Threading.Tasks.Task<Web_Manager.RemoteManager.GetSentinelListResponse> GetSentinelListAsync(Web_Manager.RemoteManager.GetSentinelListRequest request);

}

And concrete generated implementation of IRemoteManager, - RemoteManagerClient:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class RemoteManagerClient : System.ServiceModel.DuplexClientBase<Web_Manager.RemoteManager.IRemoteManager>, Web_Manager.RemoteManager.IRemoteManager {
             [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
    System.Threading.Tasks.Task<Web_Manager.RemoteManager.GetSentinelListResponse> Web_Manager.RemoteManager.IRemoteManager.GetSentinelListAsync(Web_Manager.RemoteManager.GetSentinelListRequest request) {
        return base.Channel.GetSentinelListAsync(request);
    }
}
Was it helpful?

Solution

I am not that familiar with Moq, but one thing you can try is to pass the same SentinelListRequest when you setup the mock and also when you call it.

[TestMethod]
public async Task GetAndSentSentinelList_AddedTest()
{
    var sentinelRequest = new GetSentinelListRequest();
    var mockRemoteClient = new Mock<IRemoteManager>();
    mockRemoteClient.Setup(method => method.GetSentinelListAsync(sentinelRequest))
        .Returns(Task.FromResult(new GetSentinelListResponse(new RemoteManager.SentinelItem[4]
        {
            new RemoteManager.Item() {Id = "Identity_Host"},
            new RemoteManager.Item() {Id = "Nose_Host"},
            new RemoteManager.Item() {Id = "Sunny_Host"},
            new RemoteManager.Item() {Id = "Ups_Supsu"}
        })));


    var rep = new Repository(hub)
    {
        Client = mockRemoteClient.Object,
        Sentinels = new List<SentinelItem>()
        {
            new Item() {Id = "Identity_Host"},
            new Item() {Id = "Nose_Host"},
            new Item() {Id = "Sunny_Host"}
        }
    };

    var res = await rep.Client.GetSentinelListAsync(sentinelRequest);

    //Assert
    Assert.IsNotNull(res );
}

Another option might be to specify to ignore the arguments to the call using something like this

mockRemoteClient.Setup(method => method.GetSentinelListAsync(It.IsAny<SentinelGetRequest>()))
            .Returns(Task.FromResult(new GetSentinelListResponse(new RemoteManager.SentinelItem[4]
            {
                new RemoteManager.Item() {Id = "Identity_Host"},
                new RemoteManager.Item() {Id = "Nose_Host"},
                new RemoteManager.Item() {Id = "Sunny_Host"},
                new RemoteManager.Item() {Id = "Ups_Supsu"}
            })));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top