假设我有以下界面:
public interface ITeam
{
bool HasPlayer(IPlayer player);
void AddPlayer(IPlayer player);
}
Run Code Online (Sandbox Code Playgroud)
我目前有一个测试看起来类似于(使用 AutoMoq):
[Theory]
[MyAutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(Player player, Mock<ITeam> mockedTeam)
{
player.RosterToTeam(mockedTeam.Object);
mockedTeam.Verify(team => team.AddPlayer(player), Times.Once);
}
Run Code Online (Sandbox Code Playgroud)
然而,我的代码中的一个先决条件是,调用时HasPlayer必须返回false才能通过测试RosterToTeam。
这可以通过创建ICustomization并直接组合正确的行为来解决,例如:
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)));
}
}
Run Code Online (Sandbox Code Playgroud)
但是,我希望我的测试始终假设布尔方法的默认值为false。我尝试过研究ISpecimenBuilder,但找不到实现此目的的方法,因为它似乎只适用于属性、参数等。
有谁能够向我推荐一种通用设置所有布尔方法在以这种方式创建时默认返回false 的方法吗?
编辑:行为改变背后的罪魁祸首是何时ConfigureMembers = true设置的AutoMoqCustomization。
这就是我MyAutoDataAttribute目前的样子:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
对于我的用例,ConfigureMembers = true仍然需要(并且想删除TeamCustomization)。
考虑到到目前为止提出的所有内容,我相信唯一的其他选择是实现您自己的自定义AutoMoqCustomizaiton,将返回的方法bool视为特殊情况并省略设置它们。
您可以创建一个将成员重置为 return 的行为false,但是您将无法自定义模拟以返回除 之外的任何值false,因为行为在 AutoFixture 已经创建该值时起作用。
仅使用反射来创建或更改模拟对象中返回的所有成员的设置bool并不是一件容易的任务。您必须将 AutoMoq 的大部分内容复制到您的代码库中。幸运的是,我们发布 AutoFixture 所依据的许可证是所有 OSS 许可证中最宽松的,因此您可以不受任何限制地复制和分发您的自定义版本。
您可能想要更改的部分是此处的这一行,位于MockVirtualMethodsCommand. 你的版本应该看起来像这样。
foreach (var method in methods.Where(x => x.ReturnType != typeof(bool)))
Run Code Online (Sandbox Code Playgroud)
如果这是我的代码库,在这种情况下我根本不会使用模拟,并且可能会选择假货。考虑您提出的相同代码,但测试项目将ITeam在本地类中实现接口,而不是使用 Moq。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您编写的相同测试看起来像这样。
[Theory, AutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(
Player player, FakeTeam team)
{
player.RosterToTeam(team);
Assert.Contains(player, team.Players);
}
Run Code Online (Sandbox Code Playgroud)
根据您的业务,您甚至可能想要使用 的实际实现ITeam,那就没问题了。