26

I am trying to verify that an asynchronous method was called with the correct parameters. However, I get the warning:

"Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call". This warning appears on the line of code underneath the //Assert comment (below).

My test using NSubstitute is as follows:

[Test]
public async Task SimpleTests()
{
  //Arrange
  var request = CreateUpdateItemRequest();

  databaseHelperSub.ExecuteProcAsync(Arg.Any<DatabaseParams>()).Returns(Task.FromResult((object)null));

  //Act      
  await underTest.ExecuteAsync(request);

  //Assert
  databaseHelperSub.Received().ExecuteProcAsync(Arg.Is<DatabaseParams>(
    p => p.StoredProcName == StoredProcedureName
         && p.Parameters[0].ParameterName == "Param1"
         && p.Parameters[0].Value.ToString() == "Value1"
         && p.Parameters[1].ParameterName == "Param2"
         && p.Parameters[1].Value.ToString() == "Value2"));
}

The unit under test method underTest.ExecuteAsync(request) calls ExecuteProcedureAsync and performs the await:

var ds = await DatabaseHelper.ExecuteProcAsync(dbParams);

Due to the fact that with NSubstitute, the Received() is required after the execution of the unit under test. Whereas in RhinoMocks, you can expect for a call to occur before the unit under test is executed. RhinoMocks can return the Task.FromResult() whereas NSubstitute cannot.

The RhinoMocks equivalent that works is this:

[Test]
        public async Task SimpleTest()
        {
            // Arrange
            var request = new UpdateItemRequest();

            databaseHelperMock.Expect(m => m.ExecuteProcAsync(Arg<DatabaseParams>.Matches(
                p =>   p.StoredProcName == StoredProcedureName
                    && p.Parameters[0].ParameterName == "Param1"
                    && p.Parameters[0].Value.ToString() == "Value1"
                    && p.Parameters[1].ParameterName == "Param2"
                    && p.Parameters[1].Value.ToString() == "Value2
                ))).Return(Task.FromResult<object>(null));

            // Act
            await underTest.ExecuteAsync(request);

        }

I have seen that there is a workaround where you can add an extension method to remove the issue:

  public static class TestHelper
  {
    public static void IgnoreAwait(this Task task)
    {

    }
  }

Meaning my test line for NSubstitute can be executed as follows and the warning goes away:

databaseHelperSub.Received().ExecuteProcAsync(Arg.Is<DatabaseParams>(
        p => p.StoredProcName == StoredProcedureName
             && p.Parameters[0].ParameterName == "Param1"
             && p.Parameters[0].Value.ToString() == "Value1"
             && p.Parameters[1].ParameterName == "Param2"
             && p.Parameters[1].Value.ToString() == "Value2")).IgnoreAwait();
    }

However, I assumed there must be a better solution out there for this?

Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
JBond
  • 3,062
  • 5
  • 27
  • 31
  • This is a *compiler* warning saying that you forgot to put an `await` before an asynchronous method. Why don't you simply put `await` in the line after the `//Assert` ? – Panagiotis Kanavos Jul 09 '15 at 13:20
  • 2
    If you put await before the call. The test fails as it receives a null reference exception. – JBond Jul 09 '15 at 13:24
  • 3
    NSubstitute doesn't have a better way of handling this at present. See Jake's explanation here: http://stackoverflow.com/a/31021430/906 Might be worth an NSub change, I created an issue here: https://github.com/nsubstitute/NSubstitute/issues/190 – David Tchepak Jul 12 '15 at 12:15
  • might be out of context but still I feel like recommending `Moq`.. looks like you could do with Moq.Verify() – Vignesh.N Jul 17 '15 at 17:04

3 Answers3

25

As soon as you update to version 1.9.0 or higher, you'll be able to use the await without receiving a NullReferenceException.

Marcio Rinaldi
  • 3,305
  • 24
  • 23
  • Do you have any idea when 1.8.3 might be released? Am having this issue at the moment – levelnis Aug 17 '15 at 14:24
  • I don't know. You should ask David Tchepak though (through https://github.com/nsubstitute/NSubstitute/issues maybe). He's the NSubstitute's maintainer. – Marcio Rinaldi Aug 18 '15 at 12:05
  • 2
    @levelnis Just so you are aware. V1.9.0 of NSubstitute has now been released. Which contains enhancements that will resolve the problem in this stack overflow question. – JBond Sep 28 '15 at 09:55
7

Whenever the Received() predicate gets too complicated or just won't match with NSubstitute you can always capture the specified args using callbacks via When().Do() or .AndDoes(). For your use case that would go something like this

DatabaseParams receivedParms = null;
databaseHelperSub.ExecuteProcAsync(Arg.Any<DatabaseParams>())
  .Returns(Task.FromResult((object)null))
  .AndDoes(x => receivedParms = x.Arg<DatabaseParams>);

//Act      
await underTest.ExecuteAsync(request);

//Assert
receivedParms.Should().NotBeNull();
// assert your parms...
mkoertgen
  • 898
  • 6
  • 14
3

The Jake Ginnivan answer explains that for Received await is not required, however compiler doesn’t understand it.

You can safely suppress the warning

 #pragma warning disable 4014 //for .Received await is not required, so suppress warning “Consider applying the 'await' operator”
   _service.Received(totalNumber).MyMethod(Arg.Any<ParamType>());
 #pragma warning restore 4014
Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170