Существует ли среднее основание? (Модульное тестирование против интеграционного тестирования) - PullRequest
4 голосов
/ 06 июня 2011

Рассмотрим реализацию шаблона репозитория (или аналогичного). Я постараюсь сделать пример / иллюстрацию максимально краткими:

interface IRepository<T>
{
    void Add(T entity);
}

public class Repository<T> : IRepository<T>
{
    public void Add(T entity)
    {
        // Some logic to add the entity to the repository here.
    }
}

В этой конкретной реализации, репозиторий определяется интерфейсом IRepository, чтобы иметь один метод, который добавляет объект в репозиторий, тем самым делая Repository зависимым от универсального типа T (также, репозиторий должен неявно зависеть от другого типа TDataAccessLayer , поскольку абстракция - это вся точка шаблона репозитория (однако эта зависимость в настоящее время недоступна). На данный момент, насколько я понимаю, у меня есть два варианта: модульное тестирование и интеграционное тестирование.

В тех случаях, когда можно предположить, что интеграционное тестирование имеет большее количество движущихся частей, я бы предпочел сначала модульное тестирование, чтобы как минимум проверить базовую функциональность. Однако без создания какого-либо свойства «сущности» (универсального типа T) я не вижу способа утверждать, что какая-либо логика фактически выполняется в методе Add () реализации Repository.

Может быть, есть что-то среднее между модульным тестированием и интеграционным тестированием, которое позволяет (с помощью Reflection или другими способами) проверить, что конкретные точки выполнения были достигнуты в тестируемом модуле?

Единственное объяснение, которое я придумал для этой конкретной проблемы, - это дальнейшее абстрагирование уровня доступа к данным из хранилища, в результате чего метод Add () принимает не только аргумент сущности, но и аргумент доступа к данным. Однако мне кажется, что это может нанести ущерб цели шаблона репозитория, поскольку теперь пользователь репозитория должен знать о уровне доступа к данным.

В отношении запроса примеров:

(1) Что касается модульного тестирования, я не уверен, что что-то вроде репозитория могло бы быть модульным тестированием с моим пониманием современных методов тестирования. Поскольку репозиторий является абстракцией (оболочкой) вокруг определенного уровня доступа к данным, кажется, что единственным методом проверки был бы интеграционный тест? (Конечно, интерфейс репозитория не может быть привязан к какому-либо конкретному DAL, но любой реализованный репозиторий должен быть обязательно привязан к конкретной реализации DAL, поэтому необходимо иметь возможность проверить, что метод Add () действительно выполняет некоторую работу).

(2) Что касается интеграционного тестирования, то тест, как я понимаю, будет проверять метод Add (), выполняющий работу, фактически вызывая метод Add () (который должен добавить запись в хранилище), и затем проверьте, что данные действительно были добавлены в хранилище (или, возможно, базу данных в определенном сценарии). Это может выглядеть примерно так:

[TestMethod]
public void Add()
{
    Repository<Int32> repository = new Repository<Int32>();
    Int32 testData = 10;

    repository.Add(testData);

    // Intended to illustrate the point succinctly. Perhaps the repository Get() method would not
    // be called (and a DBCommand unrelated to the repository issued instead). However, assuming the
    // Get() method to have been previously verified, this could work.
    Assert.IsTrue(testData == repository.Get(testData));
}

Таким образом, в этом случае, предполагая, что хранилище является оберткой вокруг некоторого логического уровня базы данных, база данных фактически подвергается двойному удару во время теста (один раз во время вставки и один раз во время получения).

Теперь то, что я мог бы счесть полезным, было бы техникой проверки того, что определенный путь выполнения выбран во время выполнения. Примером может быть то, что, если передается ненулевая ссылка, убедитесь, что выбран путь выполнения A, а если передана нулевая ссылка, проверьте путь выполнения B. Кроме того, возможно, можно было бы убедиться, что конкретный запрос LINQ должен был быть выполнен. Таким образом, база данных никогда не подвергается действию во время теста (что позволяет создавать прототипы и разрабатывать реализацию без реального DAL).

Ответы [ 2 ]

2 голосов
/ 06 июня 2011

Похоже, вы описываете тестирование детали реализации, а не выполнение требований шаблона разработчиком шаблона.Неважно, были ли достигнуты «конкретные точки исполнения» в тестируемом модуле, это имеет значение только в том случае, если конкретный разработчик поддерживает контракт интерфейса.Для тестов вполне приемлемо создать сущность T для целей тестирования, вот для чего нужны макеты.

0 голосов
/ 06 июня 2011

Если вы хотите провести интеграционное тестирование, вам нужно использовать реальную базу данных.Но если вы хотите быстро проверить что-то, вы можете попробовать создать базу данных в памяти.Вопрос в том, что вы можете проверить, а что нет.Пока ваш код доступа к базе данных специфичен для дБ, вы используете внешнюю систему (чтобы оставаться в модульном тесте), которую вы должны высмеивать.Но так как вы действительно хотите знать, попадают ли ваши данные в базу данных, вам нужно проверить их на реальной базе данных.

Но если вы используете некоторую абстракцию БД, например, ORM, вы можете использовать ORM mapper и проверить, еслипо крайней мере, отображение работает правильно.Затем преобразователь ORM может использовать базу данных в памяти для ваших тестов, чтобы проверить, работает ли преобразователь ORM должным образом.

Если вы не используете преобразователь ORM и создаете дополнительный уровень абстракции БД только для того, чтобы иметьабстракция, поэтому у вас есть код, который выполняется с единственной целью иметь ошибки, которые вы хотите обнаружить в ваших настоящих модульных тестах, не сделает вас более продуктивным.

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