Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I verify mocked async method called with correct expression?

I have written some tests using XUnit and Moq. One method of the mocked interface implementation is accepting a parameter of type Expression<Func<T, bool>> Everything seems to be working well but I have trouble understanding the working of verifying that the method is called with the right expression.

Given the following test, the method does not return the value specified in the setup, even though the call seems correct.:

    /// <summary>
    /// Verify that a create fails appropriately when another entity was found given the same name,
    /// Verify that the message of the exception contains the duplicate name
    /// Verify that update fails before other calls are made
    /// </summary>
    [Theory(DisplayName = "Definition Types service - Create")]
    [MemberData(nameof(DefinitionTypesTestData.SingleDefinitionType), MemberType = typeof(DefinitionTypesTestData))]
    public async Task CreateDefinitionTypeShouldThrowDuplicateTest(DefinitionType obj)
    {
        if (obj == null) throw new NullReferenceException($"Test data was null in theory 'Definition Types service - Create'");
        var crudService = new Mock<IEntityCrudService<DefinitionType>>();
        crudService.Setup(crud => crud.GetEntitiesAsync(x => x.Name == obj.Name))
            .Returns(Task.FromResult<IEnumerable<DefinitionType>>(new List<DefinitionType> {
            new DefinitionType {
                Name = "Test",
                Description = "Test",
                DisplayName = "Test",
                ID = Guid.NewGuid()
            } }));
        IDefinitionTypesService serviceUnderTest = new DefinitionTypesService(crudService.Object);
        var exception = await Assert.ThrowsAsync<EntityDuplicationException>(() => serviceUnderTest.InsertDefinitionTypeAsync(obj));
        Assert.Contains("Definition type", exception.DisplayMessage);
        Assert.Contains(obj.Name, exception.DisplayMessage);
        crudService.Verify(crud => crud.GetEntitiesAsync(x => x.Name == obj.Name), Times.Once);
        crudService.VerifyNoOtherCalls();
    }

I have the following implementation for InsertDefinitionType(DefinitionType obj):

async Task IDefinitionTypesService.InsertDefinitionTypeAsync(DefinitionType obj)
    {
        var definitiontypes = await _definitionTypeService.GetEntitiesAsync(x => x.Name == obj.Name);

        if(definitiontypes.Any())
        {
            throw new EntityDuplicationException("Definition type", name: obj.Name);
        }

        try
        {
            await _definitionTypeService.CreateAsync(obj);
        }
        catch (EntityNotSavedException exc)
        {
            exc.EntityType = "Definition type";
            throw exc;
        }
    }

When I change my setup as follows I do get the result, but in my verify function it says the function is never called (or at least with the given expression). :

crudService.Setup(crud => crud.GetEntitiesAsync(It.IsAny<Expression<Func<DefinitionType, bool>>>()))
    .Returns(Task.FromResult<IEnumerable<DefinitionType>>(new List<DefinitionType> {
        new DefinitionType {
            Name = "Test",
            Description = "Test",
            DisplayName = "Test",
            ID = Guid.NewGuid()
        } }));

Right now I have also changed the verification to be more generic:

crudService.Verify(crud => crud.GetEntitiesAsync(It.IsAny<Expression<Func<DefinitionType, bool>>>()), Times.Once);

Now my test passes, but I really want to verify the method is called correctly instead of being called at all. How do I solve this the easiest / best way?

like image 427
Bas Kooistra Avatar asked Feb 21 '19 13:02

Bas Kooistra


People also ask

How can you check if a method was called using a mock?

To check if a method was called on a mocked object you can use the Mockito. verify method: Mockito. verify(someMock).

What happens when you call async method?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

How do you call async method?

The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.

Which method calls the async task?

A synchronous method calls an async method, obtaining a Task . The synchronous method does a blocking wait on the Task .


1 Answers

You will need to be more specific about the expression used in the setup.

Use It.Is<T>() and invoked the expression for the setup and verification.

[Theory(DisplayName = "Definition Types service - Create")]
[MemberData(nameof(DefinitionTypesTestData.SingleDefinitionType), MemberType = typeof(DefinitionTypesTestData))]
public async Task CreateDefinitionTypeShouldThrowDuplicateTest(DefinitionType obj) {

    if (obj == null) throw new NullReferenceException($"Test data was null in theory 'Definition Types service - Create'");

    //Arrange
    var crudService = new Mock<IEntityCrudService<DefinitionType>>();

    var list = new List<DefinitionType>() {
        new DefinitionType {
            Name = "Test",
            Description = "Test",
            DisplayName = "Test",
            ID = Guid.NewGuid()
        }
    };

    crudService
        .Setup(_ => _.GetEntitiesAsync(It.Is<Expression<Func<DefinitionType, bool>>>(exp => exp.Compile()(obj))))
        .ReturnsAsync(list);

    IDefinitionTypesService serviceUnderTest = new DefinitionTypesService(crudService.Object);

    //Act
    var exception = await Assert.ThrowsAsync<EntityDuplicationException>(() => serviceUnderTest.InsertDefinitionTypeAsync(obj));

    //Assert
    Assert.Contains("Definition type", exception.DisplayMessage);
    Assert.Contains(obj.Name, exception.DisplayMessage);
    crudService.Verify(_ => _.GetEntitiesAsync(It.Is<Expression<Func<DefinitionType, bool>>>(exp => exp.Compile()(obj))), Times.Once);
    crudService.VerifyNoOtherCalls();
}

Pay special attention to the expression used in the setup and verification.

_ => _.GetEntitiesAsync(It.Is<Expression<Func<DefinitionType, bool>>>(exp => exp.Compile()(obj)))
like image 126
Nkosi Avatar answered Oct 26 '22 16:10

Nkosi