Как я могу модульно протестировать фильтр запросов MongoDB?

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