我正在用c#编写一个Discord机器人项目,目前正在尝试使用NUnit和Moq对系统进行单元测试。对于其中一项功能,我需要访问服务器角色,这由
完成
client.Guilds.First().Roles.Where(r => r.Name.Equals(roleName)).FirstOrDefault();
(ListManager.ModifyPermission,第116行ListManager.cs)。
client
对象是Discord.Net库中DiscordSocketClient的一个实例,并传递给该类的构造函数,并通过依赖注入来访问角色。因此,具体地说,我需要client.Guilds.First().Roles
返回一个Collection,其中填充了两个模拟角色。但是我的问题是,Moq不支持模拟非虚拟类,例如SocketRole或SocketGuild(公会对象的容器)。它们各自实现一个对应的接口(IRole,IGuild),但是无法从该接口转换为其相关类。
The entire code can be accessed here。受影响的是ListManager类。 ListManagerTestsHelper。GetDiscordSocketClient是模拟客户端的地方。我已经考虑过将现有角色列表直接传递到ListManager类中,但是依赖注入是在DiscordSocketClient连接之前设置的,因此在设置时没有分配任何角色。
编辑:
通常,我只需要知道,是否有一种方法可以从模拟对象返回Collection
答案 0 :(得分:0)
也许您可以更改字段:
private readonly DiscordSocketClient client;
将ListManager
中的放入公会,即:
private readonly IReadOnlyCollection<IGuild> guilds;
然后当然要相应地更改其构造函数。
然后您只需:
guilds.First().Roles.Where(r => r.Name.Equals(roleName)).FirstOrDefault()
或等效地:
guilds.First().Roles.FirstOrDefault(r => r.Name == roleName)
我认为从“真实”代码构造client.Guilds
时,您应该能够传递ListManager
。这是因为IReadOnlyCollection<out T>
类型具有很好的协方差。
在测试中,您只需通过new[] { guildMoq1.Object, guildMoq2.Object, }
,其中guildMoq1
等是Mock<IGuild>
,就可以根据需要设置.Roles
(同样,您应该能够仅使用new [] { ... }
来设置.Roles
)。
编辑:
也许在ListManager
中使用Func<>
:
private readonly Func<IReadOnlyCollection<IGuild>> getGuilds;
,并在构造函数的签名中使用相同的Func<>
。然后可以将该构造函数参数指定为:
() => client.Guilds
来自您的“真实代码”。这样,您就可以在尚未准备好ListManager
的时候构造client.Guilds
。 client
引用将被Func<>
(关闭语义)“捕获”。
在测试中,您可以使用:
() => new[] { guildMoq1.Object, guildMoq2.Object, }
guildMoq1
等与以前一样?
当然ListManager
中的行现在看起来像这样:
getGuilds().First().Roles.FirstOrDefault(r => r.Name == roleName)