I'm using AutoFixture to create a service which has a repository dependency injected through the constructor. When using fixture.Create<>, a mock repository is automatically provided for the dependency. I'd like to use CallBase on the mock repository so that it calls the method on the actual underlying class. Is this possible? The methods are defined as virtual so that Moq can override them. Below is a basic example and a test with AutoFixture which fails. Then the same test is shown using Moq directly which succeeds. The testing library being used is xUnit. Assume the interfaces have the shown methods defined.
Service:
public class PersonService : IPersonService {
private readonly IPersonRepository _personRepository;
public PersonService(IPersonRepository personRepository) {
_personRepository = personRepository;
}
public virtual string GetPersonName(int id) => _personRepository.GetPersonName(id);
}
Repository:
public class PersonRepository : IPersonRepository {
public virtual string GetPersonName(int id) => Data.People[id].FirstName!;
}
Data:
public static class Data {
public static List<Person> People { get; } = new() {
new Person { FirstName = "Jack" },
new Person { FirstName = "Jill" }
};
}
Person:
public class Person {
public string? FirstName { get; set; }
}
Failing Test with AutoFixture: result is null, the GetPersonName method on the repo is never called via CallBase
[Fact]
public void AutoFixture_Test_CallBase() {
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());
var mockPersonRepository = fixture.Freeze<Mock<PersonRepository>>();
mockPersonRepository.Setup(r => r.GetPersonName(It.IsAny<int>())).CallBase();
var sut = fixture.Create<PersonService>();
var result = sut.GetPersonName(1);
Assert.Equal(Data.People[1].FirstName, result);
}
The same test works fine using Moq without AutoFixture:
[Fact]
public void Moq_Test_CallBase() {
var mockPersonRepository = new Mock<PersonRepository>();
mockPersonRepository.Setup(r => r.GetPersonName(It.IsAny<int>())).CallBase();
var sut = new PersonService(mockPersonRepository.Object);
var result = sut.GetPersonName(1);
Assert.Equal(Data.People[1].FirstName, result);
}
CodePudding user response:
The problem is that when PersonService
is created by AutoFixture, it uses the Mock<IPersonRepository>
to build up an IPersonRepository
but in your code it is not defined so it uses the default.
Since you want to setup the Mock<IPersonRepository>
with CallBase
, one approach would be to freeze a Mock<PersonRepository>
and then, whenever an IPersonRepository
is required by AutoFixture to build up any other object that depends on it (like the PersonService
) you call fixture.Create<PersonRepository>()
So it'd be like this:
[Fact]
public void AutoFixture_Test_CallBase()
{
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization());
var mockPersonRepository = fixture.Freeze<Mock<PersonRepository>>();
mockPersonRepository
.Setup(r => r.GetPersonName(It.IsAny<int>()))
.CallBase();
fixture.Register<IPersonRepository>(() => fixture.Create<PersonRepository>());
var sut = fixture.Create<PersonService>();
var result = sut.GetPersonName(1);
Assert.Equal(Data.People[1].FirstName, result);
}