Попытка создать Mock Object для DiscordSocketClient и его свойства - PullRequest
0 голосов
/ 08 октября 2018

Я работаю над проектом Discord Bot, написанным на c #, и в настоящее время пытаюсь реализовать модульные тесты для системы, используя NUnit и Moq.Для одной из функций мне нужно получить доступ к ролям сервера, что делаетсяclient.Guilds.First().Roles.Where(r => r.Name.Equals(roleName)).FirstOrDefault(); (ListManager.ModifyPermission, строка 116 ListManager.cs).Объект client является экземпляром DiscordSocketClient из библиотеки Discord.Net и передается в конструктор класса, доступ к ролям которого осуществляется посредством внедрения зависимостей.В частности, мне нужно client.Guilds.First().Roles, чтобы вернуть коллекцию, заполненную двумя насмешливыми ролями.Моя проблема, однако, в том, что Moq не поддерживает моделирование не виртуальных классов, таких как SocketRole или SocketGuild (контейнер для объекта гильдии).Каждый из них реализует соответствующий интерфейс (IRole, IGuild), однако невозможно преобразовать интерфейс из соответствующего класса. Полный код доступен здесь .Затрагивается класс ListManager . ListManagerTestsHelper .GetDiscordSocketClient - это место, где должно происходить издевательство над клиентом.Я уже думал о передаче списка существующих ролей непосредственно в класс ListManager, однако внедрение зависимостей настраивается до подключения DiscordSocketClient, поэтому ему не назначаются роли на момент установки.

Edit: Как правило, мне нужно только знать, есть ли способ вернуть Collection из смоделированного объекта, даже если фактический объект должен возвращать Collection , потому что Интерфейс содержит все данные, необходимые для теста ифактический метод, который использует его.

1 Ответ

0 голосов
/ 08 октября 2018

Может быть, вы можете изменить поле:

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)
...