Mocking сложный объект - тестирование с Moq - PullRequest
0 голосов
/ 04 сентября 2018

У меня следующая структура объекта:

public class A
{
    //...
    public B b;
    //...
}

public class B
{
    //...
    public C c;
    //...
}

public class C
{
    //...
    //...
}

Я хочу создать несколько тестов для написания кода доступа к базе данных. Для этого перед каждым тестом я добавлю в базу данных некоторые необходимые данные (я использую свободный nHibernate).

Итак, чтобы протестировать какой-либо метод базы данных, выполняющий некоторые действия над объектом A, мне нужно сохранить объект класса A в базе данных

//...
var a = new A();
session.Save(a);
//...

Что приводит к исключению нулевой ссылки, поскольку объекты классов B и C также являются объектами базы данных, которые не могут быть обнуляемыми.

Мой вопрос заключается в том, как избежать такого исключения элегантным способом, используя Moq. В моей реальной проблеме деревья объектов намного сложнее, чем в этом упрощенном вопросе.

1 Ответ

0 голосов
/ 04 сентября 2018

У вас есть 2 варианта:

  1. Вы идете против реального Db = интеграционного тестирования, которое гарантировало бы правильную работу ограничений FK, каскадного удаления и т. Д. Я бы предложил создать класс DemoDataDbInit, который бы выполнял вставку реальных Db ваших исходных демонстрационных данных. Для тестов вы должны вызывать это один раз перед первым тестом, сохранить снимок (клон) «чистого» инициализированного БД, затем для последующих тестов вы просто прикрепите этот снимок (при условии, что вы используете MSSql), так что ваши тесты не зависят друг от друга (у вас есть чистый Db для каждого теста). Вы можете повторно использовать тот же класс DemoDataDbInit в своем процессе CI, чтобы иметь некоторые исходные фиктивные данные после повторного развертывания приложения. Тесты просто предполагают, что эти данные есть в БД. Также здесь учитывается Докер . К сожалению, я забыл название одного инструмента - это была очень быстрая БД в памяти, доступная как пакет nuget, который был совместим с EF & nHib, пригодный для тестов - это обеспечило бы некоторое ускорение (я полагаю, что это не был SQLite в режиме в памяти).

  2. Вы идете против насмешки. Я бы посоветовал вам создать класс InMemoryDb, который будет фабричным, с внутренним использованием Moq (или других средств насмешки) для создания для вас установки ISession.

    • session.Save(entity) добавит объект в список (смоделированный IRepository) в фоновом режиме
    • Было бы несколько предустановленных групп с некоторыми часто полезными стандартными данными

    vas session = new InMemoryDb.TwoCustomersWithThreeOrdersEach();

    • Он будет представлять все сущности как свойства экземпляра с подходящим именем, таким как Customer1Order1, чтобы вы могли легко получить к ним доступ для дополнительного упорядочивания или утверждения.
    • Если вам так хочется, строитель, позволяющий написать new InMemoryDb.TwoCustomersWithThreeOrdersEach().WithCommentOnEachOrder("my comment") и т. Д.

Вы будете использовать это InMemoryDb во всех своих тестах. Вам необходимо вручную установить эти двунаправленные отношения внешних ключей / свойства навигации вручную - чтобы ваши запросы не зависали - как часть реализации InMemoryDb.

В моем текущем проекте мы успешно используем оба подхода для интеграции и модульного тестирования.

Я не уверен, что это то, на что вы надеялись, но я боюсь, что нет простого способа «высмеять себя» из ограничений, которые накладывает «настоящий» nHibernate. Либо вы идете на полную дистанцию ​​с реальной БД, либо вы идете в память с насмешками - притворяясь, что ваши отношения с ФК верны. Только для информации: EF Core имеет собственный режим в памяти .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...