0

I just had a problem with setting up a Unit Test using xUnit and Moq.

My Testee

The Method TestMethod() contains the Logic, I want to test. It is async and though returns a Task

public class Testee
{
    private readonly IDoSomething _doSomething;

    public Testee(IDoSomething doSomething)
    {
        _doSomething = doSomething;
    }

    public async Task TestMethod()
    {
        await _doSomething.DoAsync(true); // throws NullReferenceException in Unit-Test
    }
}

The Interface IDoSomething looks like this

public interface IDoSomething
{
    Task DoAsync(bool aboolean);
}

The Unit-Test

Now im Setting up my Unit-Test like this.

public class TesteeTest
{
    [Fact]
    public async Task MyTest()
    {
        var mock = new Mock<IDoSomething>();
        mock.Setup(x => x.DoAsync(It.IsAny<bool>())).Verifiable();

        var testee = new Testee(mock.Object);

        await testee.TestMethod(); // Test breaks here because it throws an exception

        mock.Verify(x => x.DoAsync(true), Times.Once);
        mock.Verify(x => x.DoAsync(false), Times.Never);
    }
}

Why am I getting this exception?

LuckyLikey
  • 3,504
  • 1
  • 31
  • 54
  • 1
    I feel although it's not an exact duplicate, it's as close as could be for the given scenario.. I don't think it's useful to have this question when the dupe target covers a lot of the required code and the NullReferenceException canonical exists – A Friend Dec 21 '18 at 10:25
  • I agree on the similarity, the difference is indeed that the suggested duplicate was missing the *Task completion*, while my question was missing the Task at all. This makes it different, because ususally, you would use `.ReturnsAsync?` to return something asynchronous, which wasn't an option, as you cannot call `ReturnsAsinc` with `void`. – LuckyLikey Dec 21 '18 at 10:42
  • @everyone As I've also marked this question as duplicate, I clearly agree with the similarity. However I don't get the reason of the downvote on this question. If someone might take the time to help me understand how to better write such a question next time. – LuckyLikey May 13 '19 at 05:11
  • I think it's an excellent question. The other questions are "how to". This one's "why null reference". I changed a method returning void to being tested from asynchronous returning Task and all my unit tests broke because I didn't do this setup – Colin Oct 09 '20 at 17:57

1 Answers1

4

The mock.Setup() is missing a return value and returns null. So in the TestMethod() the Task returned by _doSomething.DoAsync(true) is null. null gets awaited which of course results in a System.NullReferneceException.

To Fix this, I'd recommend adding .Returns(Task.CompletedTask) to the Mock-Setup.

mock.Setup(x => x.DoAsync(It.IsAny<bool>())).Returns(Task.CompletedTask).Verifiable();

As await gets compiled using the Awaiter-Pattern, there's now a Task to call .GetAwaiter() on it, to return a TaskAwaiter. These principles are better explained here:

LuckyLikey
  • 3,504
  • 1
  • 31
  • 54