假设我有以下接口:
public interface ITeam
{
bool HasPlayer(IPlayer player);
void AddPlayer(IPlayer player);
}我目前有一个测试,它看起来类似于(使用AutoMoq):
[Theory]
[MyAutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(Player player, Mock<ITeam> mockedTeam)
{
player.RosterToTeam(mockedTeam.Object);
mockedTeam.Verify(team => team.AddPlayer(player), Times.Once);
}但是,我的代码中的一个先决条件是,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)));
}
}但是,我希望我的测试总是假设布尔方法的默认值为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;
}
}对于我的用例,仍然需要ConfigureMembers = true (并且希望删除TeamCustomization)。
发布于 2022-03-29 22:01:15
考虑到到目前为止提出的所有建议,我认为唯一的其他选择是实现您自己的自定义AutoMoqCustomizaiton,它将把返回bool的方法作为一个特例,并忽略设置它们。
您可以创建一个将成员重置为返回false的行为,但是您将无法自定义您的模拟以返回除false以外的任何值,因为AutoFixture已经创建了该值。
通过使用反射为返回模拟对象中的bool的所有成员创建或更改设置并不是一项容易的任务。您必须将大量的AutoMoq复制到您的代码库中。幸运的是,我们发布AutoFixture的许可证是所有OSS许可证中最允许的,因此您可以不受任何限制地复制和分发您的自定义版本。
您可能想要更改的部分是这里行,在MockVirtualMethodsCommand中。你的版本应该是这样的。
foreach (var method in methods.Where(x => x.ReturnType != typeof(bool)))如果这是我的代码库,在这种情况下,我根本不会使用模拟,而且可能会选择假的。考虑您建议的相同代码,但是测试项目将在本地类中实现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);
}
}现在,您编写的相同的测试看起来会是这样的。
[Theory, AutoData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(
Player player, FakeTeam team)
{
player.RosterToTeam(team);
Assert.Contains(player, team.Players);
}根据您的业务,您甚至可能希望使用ITeam的实际实现,这很好。
发布于 2022-03-26 16:40:03
首先,您可能希望使用AutoMoqDataAttribute来创建ITeam接口的模拟:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture().Customize(new AutoMoqCustomization()))
{
}
}没有必要在缓冲夹具配置您的模拟。最好在测试本身(排列部分)中这样做:
[Theory, AutoMoqData]
public void ShouldRosterToTeamWhenPlayerIsNotRostered(Player player, Mock<ITeam> mockedTeam)
{
mockedTeam.Setup(t => t.HasPlayer(player)).Returns(false);
player.RosterToTeam(mockedTeam.Object);
mockedTeam.Verify(team => team.AddPlayer(player), Times.Once);
}
[Theory, AutoMoqData]
public void ShouldNotRosterToTeamWhenPlayerIsRostered(Player player, Mock<ITeam> mockedTeam)
{
mockedTeam.Setup(t => t.HasPlayer(player)).Returns(true);
player.RosterToTeam(mockedTeam.Object);
mockedTeam.Verify(team => team.AddPlayer(player), Times.Never);
}最后,简化的RoastToTeam实现:
public class Player
{
public void RosterToTeam(ITeam team)
{
if (team.HasPlayer(this))
{
return;
}
team.AddPlayer(this);
}
}发布于 2022-03-31 09:22:00
据我所见,AutoMoq直接解析返回类型。(来源在这里)
这意味着您无法使返回bool的所有方法返回false,除非注入值(fixture.Inject(false)) --当然,这意味着每次向AF请求bool时都会得到false。
在您的场景中,我所做的是创建一个空接口,该接口继承自我想要修改的行为,并注册它。
如下所示:
...
public interface ITeamFake : ITeam {} // empty interface for modifying the mock
...
// Modify ITeam behavior
fixture.Register<Mock<ITeamFake>, Mock<ITeam>>(t =>
{
t.Setup(m => m.HasPlayer(It.IsAny<IPlayer>())).Returns(true);
return t.As<ITeam>();
});不管怎么说,这对我来说是可行的。
https://stackoverflow.com/questions/71626344
复制相似问题