Say that I have the following interface:
public interface ITeam
{
bool HasPlayer(IPlayer player);
void AddPlayer(IPlayer player);
}
I currently have a test that looks something along the lines of (using AutoMoq):
[Theory]
[MyAutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(Player player, Mock<ITeam> mockedTeam)
{
player.RosterToTeam(mockedTeam.Object);
mockedTeam.Verify(team => team.AddPlayer(player), Times.Once);
}
However, a precondition in my code is that HasPlayer
must return false for the test to pass when RosterToTeam
is called.
This can be solved by creating a ICustomization
and directly composing the correct behaviour, for example:
public class TeamCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Mock<ITeam>>(composer =>
composer.Do(mock =>
mock.Setup(team => team.HasPlayer(It.IsAny<IPlayer>()))
.Returns(false)));
}
}
However, I'd like my tests always to assume that the boolean methods have a default value of false. I've tried looking into ISpecimenBuilder
, but couldn't see a way to achieve this, as it seems to only work on properties, parameters, etc.
Is anyone able to recommend me a way of generically setting up all boolean methods to return false by default when created in this fashion?
Edit: The culprit behind the behaviour change is when ConfigureMembers = true
is set for AutoMoqCustomization
.
This is what my MyAutoDataAttribute
currently looks like:
public class MyAutoDataAttribute : AutoDataAttribute
{
public MyAutoDataAttribute() : base(Create)
{
}
private static IFixture Create()
{
var fixture = new Fixture();
fixture.Customize(new AutoMoqCustomization
{
ConfigureMembers = true
});
fixture.Customize(new TeamCustomization());
return fixture;
}
}
For my use case, ConfigureMembers = true
is still needed (and would like to remove the TeamCustomization
).
Considering everything that was proposed so far, I believe the only other option is to implement your own custom AutoMoqCustomizaiton
that will treat the methods returning bool
as a special case and will omit setting them.
You could create a behavior that would reset the members to return false
, but you would not be able to then customize your mocks to return any value other than false
, since behaviors work at the moment AutoFixture has already created the value.
Creating or altering the setup for all members returning bool
in a mock object by just using reflection is not an easy task. You'll have to copy a good chunk of the AutoMoq into your code base. Luckily for you the license under which we publish AutoFixture is the most permissive of all OSS licenses, so you can copy and distribute your custom version without any restrictions.
The part that you'll likely want to change is this line here, in MockVirtualMethodsCommand
. Your version should look something like this.
foreach (var method in methods.Where(x => x.ReturnType != typeof(bool)))
If this was my code-base, in cases like this I would not use mocks at all, and would probably opt for fakes. Consider the same code you proposed but instead of using Moq, the test project would implement the ITeam
interface in a local class.
public class FakeTeam : ITeam
{
public List<IPlayer> Players { get; set; } = new();
public bool HasPlayer(IPlayer player)
{
return this.Players.Contains(player);
}
public void AddPlayer(IPlayer player)
{
this.Players.Add(player);
}
}
Now the same test that you wrote would look something like this.
[Theory, AutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(
Player player, FakeTeam team)
{
player.RosterToTeam(team);
Assert.Contains(player, team.Players);
}
Depending on your business you might even want to use the actual implementation of ITeam
, and that would be fine.