Вопрос

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

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

public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters)
{
    DataTable resultSet = new DataTable();
    SqlCommand queryCommand = new SqlCommand();
    try
    {
        queryCommand.Connection = ActiveConnection;
        queryCommand.CommandText = Query;

        if (Parameters != null)
        {
            foreach (SqlParameter param in Parameters)
            {
                 queryCommand.Parameters.Add(param);
            }
        }

        SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand);
        queryDA.Fill(resultSet);
    }
    catch (Exception ex)
    {
        //TODO: Improve error handling
        Console.WriteLine(ex.Message);
    }

    return resultSet;
}

Этот метод по существу принимает все необходимые фрагменты для извлечения некоторых данных из базы данных и возвращает данные в объект DataTable.

Первый вопрос, наверное, самый сложный:Что мне вообще следует проверить в такой ситуации?

Как только это будет решено, встанет вопрос о том, следует ли моделировать компоненты базы данных или попытаться протестировать их на реальной БД.

Это было полезно?

Решение

Что вы тестируете?

Есть три возможности, на мой взгляд:

А.Вы тестируете класс DAO (объект доступа к данным), проверяя, правильно ли он маршалирует значения/параметры, передаваемые в базу данных, а также правильно маршалирует/преобразует/упаковывает результаты, полученные из базы данных.

В этом случае вам вообще не нужно подключаться к базе данных;вам просто нужен модульный тест, который заменяет базу данных (или промежуточный уровень, например, JDBC, (N)Hibernate, iBatis) макетом.

Б.Вы проверяете синтаксическую правильность (сгенерированного) SQL.

В этом случае, поскольку диалекты SQL различаются, вы хотите запустить (возможно, сгенерированный) SQL для правильной версии вашей СУБД, а не пытаться имитировать все особенности вашей СУБД (и чтобы любые обновления СУБД, которые изменяют функциональность, были обнаружены ваши тесты).

С.Вы тестируете смысловой правильность вашего SQL, т. е. то, что для данного базового набора данных ваши операции (доступ/выбор и мутации/вставки и обновления) создают ожидаемый новый набор данных.

Для этого вам нужно использовать что-то вроде dbunit (который позволяет вам установить базовый уровень и сравнить набор результатов с ожидаемым набором результатов) или, возможно, полностью провести тестирование в базе данных, используя метод, который я описываю здесь: Лучший способ протестировать SQL-запросы.

Другие советы

Вот почему (IMHO) модульные тесты могут иногда создавать ложное чувство безопасности со стороны разработчиков. По моему опыту работы с приложениями, которые обращаются к базе данных, ошибки обычно являются результатом непредвиденного состояния данных (необычные или отсутствующие значения и т. Д.). Если вы регулярно макетируете доступ к данным в своих модульных тестах, вы будете думать, что ваш код работает отлично, хотя на самом деле он все еще уязвим для такого рода ошибок.

Я думаю, что ваш лучший подход - это иметь удобную тестовую базу данных, заполненную кучами дерьмовых данных, и запускать тесты компонентов базы данных на эту тему. При этом помните, что ваши пользователи будут намного лучше, чем вы, облажаясь в своих данных.

Весь смысл модульного теста состоит в том, чтобы протестировать модуль (дух) в изоляции. Весь смысл вызова базы данных состоит в том, чтобы интегрировать с другим модулем (базой данных). Ergo: нет смысла проверять вызовы из базы данных.

Однако вы должны выполнять интеграционные тестовые вызовы базы данных (и вы можете использовать те же инструменты, которые вы используете для модульного тестирования, если хотите).

Ради любви к Богу, не проверяйте живую, уже заполненную базу данных. Но ты это знал.

Как правило, у вас уже есть представление о том, какие данные будет собирать каждый запрос, независимо от того, проводите ли вы аутентификацию пользователей, просматриваете записи в телефонной книге / оргструктуре или что-то еще. Вы знаете, какие поля вас интересуют, и знаете, какие ограничения на них существуют (например, UNIQUE , NOT NULL и т. Д.). Вы проводите модульное тестирование своего кода, который взаимодействует с базой данных, а не с самой базой данных, поэтому подумайте о том, как протестировать эти функции. Если поле может иметь значение NULL , у вас должен быть тест, который гарантирует, что ваш код правильно обрабатывает значения NULL . Если одно из ваших полей является строкой ( CHAR , VARCHAR , TEXT и & c), проверьте, чтобы убедиться, что вы обрабатываете экранированный символы правильно.

Предположим, что пользователи попытаются поместить что-либо * в базу данных, и соответственно сгенерируйте контрольные примеры. Вы захотите использовать для этого фиктивные объекты.

* Включая нежелательный, злонамеренный или неверный ввод.

Вы можете выполнить модульное тестирование всего, кроме: queryDA.Fill (resultSet);

Как только вы выполните queryDA.Fill (resultSet) , вам придется либо смоделировать / подделать базу данных, либо проводить интеграционное тестирование.

Я, например, не считаю интеграционное тестирование плохим, просто оно будет отлавливать ошибки другого рода, иметь разные шансы ложных отрицательных и ложных положительных результатов, и вряд ли будет проводиться очень часто, потому что это так медленно

Если бы я проводил модульное тестирование этого кода, я бы проверил правильность построения параметров. Создает ли компоновщик команд правильное количество параметров? У них всех есть ценность? Правильно ли обрабатываются нули, пустые строки и DbNull?

Фактически, заполнение набора данных - это проверка вашей базы данных, которая является нестабильным компонентом вне области вашего DAL.

Строго говоря, тест, выполняющий запись/чтение из базы данных или файловой системы, не является модульным тестом.(Хотя это может быть интеграционный тест и он может быть написан с использованием NUnit или JUnit).Юнит-тесты предназначены для проверки работы одного класса, изолируя его зависимости.Итак, когда вы пишете модульный тест для уровней интерфейса и бизнес-логики, вам вообще не нужна база данных.

Хорошо, но как провести модульное тестирование уровня доступа к базе данных?Мне нравятся советы из этой книги: Тестовые шаблоны xUnit (ссылка указывает на главу книги «Тестирование с БД».Ключи:

  • используйте обратные тесты
  • не пишите слишком много тестов в вашем тесте доступа к данным, потому что они будут работать намного медленнее, чем ваши «настоящие» модульные тесты.
  • если вы можете избежать тестирования с реальной базой данных, тестируйте без базы данных

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

Что касается того, что вы будете тестировать, вы должны убедиться, что метод использует соединение из параметров, что строка запроса назначена команде, и что ваш возвращенный набор результатов совпадает с тем, который вы предоставляете через ожидание по методу Fill. Примечание. Вероятно, проще протестировать метод Get, который возвращает значение, чем метод Fill, который изменяет параметр.

Для того, чтобы сделать это правильно, вы должны использовать некоторое внедрение зависимостей (DI), а для .NET их несколько. В настоящее время я использую Unity Framework, но есть и другие, которые проще.

Вот одна ссылка с этого сайта на эту тему, но есть и другие: Внедрение зависимостей в .NET с примерами?

Это позволит вам легче макетировать другие части вашего приложения, просто создав имитируемый класс для реализации интерфейса, чтобы вы могли контролировать его реакцию. Но это также означает проектирование интерфейса.

Поскольку вы спрашивали о передовой практике, это будет один, ИМО.

Тогда не идите в БД, если вам не нужно, как и предлагается другой.

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

Редактировать: Под модульным тестом базы данных я имею в виду это, так как он предназначен только для использования t-sql для некоторой настройки, тестирования и демонтажа. http://msdn.microsoft.com/en- нас / библиотека / aa833233% 28VS.80% 29.aspx

В проекте, основанном на JDBC, соединение JDBC можно смоделировать, чтобы тесты можно было выполнять без оперативной RDBMS, с изолированным каждым тестовым примером (без конфликта данных).

Это позволяет проверить, код персистентности передает правильные запросы / параметры (например, https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala ) и обрабатывать результаты JDBC (синтаксический анализ / отображение), как и ожидалось («берет все необходимые биты и кусочки для извлечения некоторых данных из базы данных и возвращает данные в объект DataTable»).

Фреймворк, такой как jOOQ или мой фреймворк Acolyte, может использоваться для: https://github.com/cchantep/acolyte .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top