user9124444
user9124444

Reputation:

Test fails if an Exception is thrown in another part of the SUT (async flow)

I have a system like this

class Sut
{
    IRepository _repo;

    public Sut(IRepository repo) { _repo = repo; }

    async Task Handle(Request request)
    {
         var entity  = new Entity(request.Id); //=> if Id is not ok the Entity throws Exception
    
         _repo.Save(entity);
    }
}

This is the test

[Fact]
public async Task Test_Save()
{
    Mock<IRepository> repositoryMock = new Mock<IRepository>();

    repositoryMock
        .Setup(repo => repo.Save(It.IsAny<Entity>()))
        .Returns(Task.CompletedTask);

    var sut = new Sut(repositoryMock.Object);

    /// Act
    Action action = async () => await sut.Handle(new Request { Id = default(Guid)});

    /// Assert 
    Assert.Throws<Exception>(action);
    repositoryMock.Verify(repo => repo.Save(It.IsAny<Entity>(), Times.Never);
}

So what it does is, the Entity has a check for default Guid being passed to it. If default value is passed it will throw an Exception.

The exception is thrown but the test fails with this message.

Message: Assert.Throws()

Failure Expected: typeof(System.Exception)

Actual: (No exception was thrown)

The test never calls

repositoryMock.Verify(repo => repo.Save(It.IsAny<Entity>(), Times.Never);

it will break at this line

Why is that and how to solve this situation ?

Assert.Throws<Exception>(action);

UPDATE

 public Entity(Guid id)
 {
     if (default(Guid) == id) throw new Exception("cannot have a default value for id");
     Id = id;
 }

Upvotes: 2

Views: 134

Answers (1)

Nkosi
Nkosi

Reputation: 247098

The Action action = async () => ... is basically an async void, which would mean that the thrown exception wont get caught.

Change the syntax to use a Func<Task> along with Assert.ThrowsAsync

[Fact]
public async Task Test_Save() {
    //Arrange
    var repositoryMock = new Mock<IRepository>();

    repositoryMock
        .Setup(repo => repo.Save(It.IsAny<Entity>()))
        .Returns(Task.CompletedTask);

    var sut = new Sut(repositoryMock.Object);

    /// Act
    AsyncTestDelegate act = () => sut.Handle(new Request { Id = default(Guid)});

    /// Assert 
    await Assert.ThrowsAsync<Exception>(act);
    repositoryMock.Verify(repo => repo.Save(It.IsAny<Entity>(), Times.Never);
}

Reference Assert.ThrowsAsync

Upvotes: 3

Related Questions