Reputation: 139
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?
Upvotes: 13
Views: 16814
Reputation: 247631
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)))
Upvotes: 4