ChrisM ChrisM - 3 months ago 21
C# Question

Test that call is awaited using NUnit and Moq

I have a piece of production code where some long lasting task must finish before something else can be done (See example below). I am able to test the call order but not the await (which is missing in the example). What must I do to test this?

using System.Threading.Tasks;
using Moq;
using NUnit.Framework;

namespace TestingCallsAwaitedDemo
{
public interface IFoo
{
Task DoThisFirst();
}

public interface IBar
{
void ThenDoThis();
}

public class Demo
{
private readonly IFoo _foo;
private readonly IBar _bar;

public Demo(IFoo foo, IBar bar)
{
_bar = bar;
_foo = foo;
}

public async Task DoSomething()
{
_foo.DoThisFirst(); // The await is missing. How do I force its presence with a test?
_bar.ThenDoThis();
}
}

[TestFixture]
public class DemoTests
{
private Demo classUnderTest;
private Mock<IFoo> fooMock;
private Mock<IBar> barMock;

[SetUp]
public void Setup()
{
fooMock = new Mock<IFoo>();
barMock = new Mock<IBar>();
classUnderTest = new Demo(fooMock.Object, barMock.Object);
}

[Test]
public async Task MustFinishDoThisFirstBeforeCallingThenDoThis()
{
var callOrder = string.Empty;
fooMock.Setup(v => v.DoThisFirst())
.Callback(() => callOrder += "DoThisFirst >> ");
barMock.Setup(v => v.ThenDoThis())
.Callback(() => callOrder += "ThenDoThis >> ");

await classUnderTest.DoSomething();

Assert.AreEqual(
"DoThisFirst >> ThenDoThis >> ",
callOrder);

//How do I test the missing await?
}
}
}





Here is a solution. Thanks to @dvorn



[Test]
public async Task MustFinishDoThisFirstBeforeCallingThenDoThis()
{
var tcs = new TaskCompletionSource<bool>();
fooMock.Setup(v => v.DoThisFirst()).Returns(tcs.Task);

var task = classUnderTest.DoSomething();

Assert.IsFalse(task.IsCompleted, "Did not await 'DoThisFirst'");
fooMock.Verify(f => f.DoThisFirst(), Times.Once);
barMock.Verify(b => b.ThenDoThis(), Times.Never);

tcs.SetResult(true);
await task;

Assert.IsTrue(task.IsCompleted);
fooMock.Verify(f => f.DoThisFirst(), Times.Once);
barMock.Verify(b => b.ThenDoThis(), Times.Once);
}

Answer

TaskCompletionSource<bool> tcs;

Setup that fooMock returns tcs.Task

var task = classUnderTest.DoSomething();

Assert that task is not completed.

tcs.SetResult(true).

Now await task.