Подтвердить что ты не робот

Как написать модульные тесты для вызовов базы данных

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

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

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.

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

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

4b9b3361

Ответ 1

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

Есть три возможности: от головы:

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Ответ 4

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

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

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

* Включение нежелательного, вредоносного или недопустимого ввода.

Ответ 5

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

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

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

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

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

Ответ 6

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

ОК, но как вы тестируете уровень доступа к базе данных? Мне нравится совет из этой книги: xUnit Test Patterns (ссылка указывает на книгу "Тестирование с DB". Ключи:

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

Ответ 7

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

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

Ответ 8

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

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

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

Поскольку вы спросили о лучших практиках, это будет один, IMO.

Тогда, не отправляясь в db, если вам не нужно, как и предполагалось, это другое.

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

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

Ответ 9

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

Это позволяет проверить, код персистентности передает правильные запросы/параметры (например, 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.