尝试为DiscordSocketClient及其属性创建模拟对象

时间:2018-10-08 06:43:02

标签: c# nunit moq discord.net

我正在用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 ,即使实际对象应该返回Collection ,因为接口包含测试和测试所需的所有数据。使用它的实际方法。

1 个答案:

答案 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.Guildsclient引用将被Func<>(关闭语义)“捕获”。

在测试中,您可以使用:

() => new[] { guildMoq1.Object, guildMoq2.Object, }

guildMoq1等与以前一样?

当然ListManager中的行现在看起来像这样:

getGuilds().First().Roles.FirstOrDefault(r => r.Name == roleName)