#c# #.net #mongodb #unit-testing #mongodb-query
#c# #.net #mongodb #модульное тестирование #mongodb-запрос
Вопрос:
У меня есть сценарий, похожий на следующий (упрощенный для наглядности):
public class ExampleRepository
{
private readonly IMongoDatabase _db;
public ExampleRepository(IMongoClient mongoClient)
{
this._db = mongoClient.GetDatabase("database");
}
public async Task<IEnumerable<Item>> GetItemsUsingFilter(Guid ownerId, DateTimeOffset createdSince, IEnumerable<ItemType> typesToIgnore)
{
var cursor = await this._db.GetCollection<Item>("Items")
.FindAsync(item =>
item.OwnerId == ownerId
amp;amp;
item.CreatedDate >= createdSince
amp;amp;
!typesToIgnore.Contains(item.Type)
.SortByDescending(item => item.UserRating)
.Limit(10);
return await cursor.ToListAsync();
}
}
Я хочу написать модульные тесты для проверки правильности моего фильтра запросов (а также сортировки и ограничения вызовов), но я не могу понять, как настроить тестовые данные для этого.
Я пытался имитировать базу данных IMongoDatabase и настроить вызов getCollection для возврата макета IMongoCollection, но это неправильный подход, поскольку мне нужно вызвать вызов FindAsync для реального MongoCollection.
Я изучил возможность изменения репозитория, разделив вызов getCollection, а затем применив фильтрацию с использованием стандартного LINQ, но я не хочу возвращать всю мою коллекцию из базы данных, а затем запрашивать ее на уровне репозитория.
Я нашел несколько примеров модульного тестирования MongoDB, но все они были связаны с издевательством над вызовом FindAsync, что мне не нужно делать.
Я также рассматривал возможность объявления моего фильтра как выражения<Func<Item, Boolean>> в отдельном классе, чтобы я мог протестировать его изолированно, но я хотел бы изучить другие варианты, прежде чем идти по этому пути, поскольку это усложняет уровень репозитория.
Ответ №1:
Я не работал с MongoDB, но вот мой подход с точки зрения чистого модульного тестирования:
- Обратите внимание, что тестируемый метод имеет четыре входа: зависимость IMongoClient (которую я рассматриваю как косвенный ввод) и три аргумента (прямые вводы).
- Создайте объект, реализующий IMongoDatabase (MD), и смоделируйте IMongoClient, чтобы вернуть его.
- Создайте объект, реализующий IMongoCollection (MC), и верните его методом getCollection MD; MC на самом деле является вашими тестовыми данными.
- Определите три аргумента для передачи тестируемому методу.
Теперь вы можете выполнить тест.
Пожалуйста, обратите внимание, что реализация MC включает в себя такие методы, как FindAsync, но это нормально, поскольку ваша цель — не тестировать функционирование MongoDB, а тестировать поток и поведение вашего метода.
Взгляните на этот упрощенный пример (основанный на вашем коде):
public async Task<IEnumerable<string>> GetItemsUsingFilter()
{
var cursor = await this._db.GetCollection<string>("Items")
.SortByDescending()
.Limit(10);
return await cursor.ToListAsync();
}
Если getCollection возвращает в рабочей среде 12 элементов, «A».. «L», мы хотели бы, чтобы возвращалось «L»..»C». Более того, мы хотим, чтобы наш тест завершился неудачей, если, например, какой-либо другой разработчик ошибочно переместит Limit перед SortByDescending (что приведет к тому, что производственный код вернет «J»..»A»).