Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq with Task await

Since I have converted my WCF methods to Async, my unit tests have failed, and I can't figure out the correct syntax to get them to work.

Cllient proxy class

 public interface IClientProxy
{
     Task DoSomething(CredentialDataList credentialData, string store);
}

service class

  public class CredentialSync : ICredentialSync
{
    private ICredentialRepository _repository;

    private IClientProxy _client;

    public CredentialSync()
    {
        this._repository = new CredentialRepository();
        this._client = new ClientProxy();
    }

    public CredentialSync(ICredentialRepository repository, IClientProxy client)
    {
        this._repository = repository;
        this._client = client;
    }

   public async Task Synchronise(string payrollNumber)
    {
        try
        {
            if (string.IsNullOrEmpty(payrollNumber))
            {
                .... some code
              }
            else
            {
                CredentialDataList credentialData = new CredentialDataList();
                List<CredentialData> credentialList = new List<CredentialData>();

                // fetch the record from the database
                List<GetCredentialData_Result> data = this._repository.GetCredentialData(payrollNumber);
                var pinData = this._repository.GetCredentialPinData(payrollNumber);

                // get the stores for this employee
                var storeList = data.Where(a => a.StoreNumber != null)
                    .GroupBy(a => a.StoreNumber)
                    .Select(x => new Store { StoreNumber = x.Key.ToString() }).ToArray();

                var credential = this.ExtractCredentialData(data, pinData, payrollNumber);

                credentialList.Add(credential);
                credentialData.CredentialList = credentialList;

                foreach (var store in storeList)
                {       
                  //this line causes an Object reference not set to an instance of an object error
                   await  _client.DoSomething(credentialData, store.StoreNumber);

                }
            }
        }
        catch (Exception ex)
        {
            throw new FaultException<Exception>(ex);
        }
    }

Test Class

 /// </summary>
[TestClass]
public class SynchTest
{

    private Mock<ICredentialRepository> _mockRepository;
    private Mock<IClientProxy> _mockService;

    [TestInitialize]
    public void Setup()
    {
       ... some setups for repository which work fine
    }

[TestMethod]      
    public async Task SynchroniseData_WithOneEmployee_CallsReplicateService()
    {
        this._mockService = new Mock<IClientProxy>();
        this._mockService.Setup(x=>x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()));
        // arrange
        string payrollNumber = "1";
        CredentialSync service = new CredentialSync(this._mockRepository.Object, this._mockService.Object);

        // act
        await service.Synchronise(payrollNumber);

        // assert                 
        this._mockService.VerifyAll();
    }

The error is when ClientProxy.DoSomething is called:

Object reference not set to an instance of an object

The parameters are both fine.

If I convert my ClientProxy.DoSomething method to a synchronous method (public void DoSomething(...) )the code works fine, but I do need this to be called asynchronously

like image 313
jazza1000 Avatar asked Feb 18 '15 10:02

jazza1000


People also ask

How to mock async method in c#?

In this method, we set up an asynchronous mock call to the GetByIdAsync method from the IFakeDbArticle interface. This method returns the first article from the list we created inside the FakeDb class. When we mock an asynchronous method, instead of using a standard Return(...) method, we use the ReturnAsync(...)

How do I return a task in C#?

You use the void return type in asynchronous event handlers, which require a void return type. For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited.

What is setup in MOQ?

'Setup' mocks a method and 'Returns' specify what the mocked method should return.


3 Answers

DoSomething returns null instead of returning a Task, and so you get an exception when awaiting it. You need to specify when building the mock that it should return a Task.

In this case it seems that you can simply return an already completed task using Task.FromResult so the mock setup should look like this:

this._mockService.Setup(...).Returns(Task.FromResult(false));

Beginning with the next version of .Net (4.6) you can use Task.CompletedTask like this:

this._mockService.Setup(...).Returns(Task.CompletedTask);
like image 123
i3arnon Avatar answered Nov 14 '22 12:11

i3arnon


You can reduce the amount of clutter in the code by using ReturnsAsync

this._mockService.Setup(...).ReturnsAsync(false);

This way you can remove the Task.FromResult part of the code

like image 34
knorman Avatar answered Nov 14 '22 10:11

knorman


I think you need to return the Task from the DoSomething mock

this._mockService.Setup(x => x.DoSomething(It.IsAny<CredentialDataList>(), It.IsAny<string>()))
    .Returns(Task.FromResult<int>(0));
like image 6
NeddySpaghetti Avatar answered Nov 14 '22 11:11

NeddySpaghetti