【问题标题】:Trying to create Mock Object for DiscordSocketClient and its properties尝试为 DiscordSocketClient 及其属性创建模拟对象
【发布时间】:2018-10-08 16:43:32
【问题描述】:

我正在开发一个用 c# 编写的不和谐机器人项目,目前正在尝试使用 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 来返回一个包含两个模拟角色的集合。然而,我的问题是,Moq 不支持模拟 SocketRole 或 SocketGuild(公会对象的容器)等非虚拟类。它们每个都实现了一个相应的接口(IRole、IGuild),但是不可能从该接口转换为它的相关类。
The entire code can be accessed here.受影响的是ListManager 类。 ListManagerTestsHelper.GetDiscordSocketClient 是模拟客户端的地方。我已经考虑过将现有角色的列表直接传递给 ListManager 类,但是依赖注入是在 DiscordSocketClient 连接之前设置的,因此在设置时没有分配给它的角色。

编辑:
一般来说,我只需要知道,是否有办法从模拟对象返回 Collection,即使实际对象应该返回 Collection,因为接口包含测试所需的所有数据和使用它的实际方法。

【问题讨论】:

    标签: c# nunit moq discord.net


    【解决方案1】:

    也许您可以更改字段:

    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)
    

    我认为当您从“真实”代码构造ListManager 时,您应该能够传入client.Guilds。这是因为 IReadOnlyCollection&lt;out T&gt; 类型的协方差很好。

    在您的测试中,您可以只传递一个new[] { guildMoq1.Object, guildMoq2.Object, },其中guildMoq1 等是一个Mock&lt;IGuild&gt;,您可以根据需要设置.Roles(同样,您应该能够只使用new [] { ... }设置.Roles)。


    编辑:

    也许在ListManager 中使用Func&lt;&gt;

    private readonly Func<IReadOnlyCollection<IGuild>> getGuilds;
    

    和构造函数签名中的Func&lt;&gt; 相同。那么构造函数参数可以给出为:

    () => client.Guilds
    

    来自您的“真实代码”。通过这种方式,您可以在 client.Guilds 尚未准备好被询问时构建您的 ListManagerclient 引用将在 Func&lt;&gt;(闭包语义)中“捕获”。

    在测试中,可以使用:

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

    guildMoq1 等在哪里?

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

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

    【讨论】:

    • 感谢您的建议。问题与在其中传递角色集合相同,在创建 ListManager 时,客户端尚未连接,因此没有可用的 SocketGuild 集合。但是由于这会导致很多问题,我将尝试重构 ListManager 类,这样我就不需要访问类内部的客户端数据。希望这会成功。无论如何,感谢您的帮助。
    • 啊,那你会想要Func&lt;IReadOnlyCollection&lt;IGuild&gt;&gt;,也许吧?将编辑。
    猜你喜欢
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 2017-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 2022-01-23
    相关资源
    最近更新 更多