2009-04-24 3 views
20

Я пишу unit-тесты для приложения, которое использует базу данных, и я хотел бы иметь возможность запускать приложение против некоторых данных образца/теста, m не уверены в наилучшем способе настройки исходных тестовых данных для тестов.Unit-Testing: настройка базы данных для тестов

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

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

Любые предложения или статьи, которые могут указывать на меня в правильном направлении?

Спасибо!

--EDIT--

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

ответ

14

Вот общий подход, который я пытаюсь использовать. Я представляю тесты на трех или четырех уровнях :: unit-tests, тесты взаимодействия, интеграционные тесты, приемочные тесты.

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

Испытания на взаимодействие проверяют взаимодействие нетривиальных взаимодействий классов. Опять же, никакой базы данных не требуется, это издевается.

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

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

+0

Как выглядят классы скрабов? Вы находите самый последний идентификатор первичного ключа и удаляете связанную запись? –

+0

Нет, на самом деле, он полностью удаляет все и загружает только то, что мне нужно для теста. –

0

Я не думаю, что есть простой способ закончить это. Вам просто нужно создать те сценарии настройки SQL-теста и тестовые сценарии с последующим тестированием. Затем вам нужно запустить эти сценарии для каждого прогона. Многие люди предлагают SQLLite для установки тестового тестирования.

+1

SQLLite имеет большое отличие от больших баз данных, поэтому нет смысла тестировать на нем –

1

Я знаю, что вы используете C#, но в Java World есть среда Spring. Он позволяет запускать мини-проекты базы данных в транзакции, и после этой транзакции вы откатываете это обратно. Это означает, что вы работаете с реальной базой данных, не касаясь состояния после завершения теста. Возможно, это может быть намеком на дальнейшее исследование на C#.

+0

FYI - весна также доступна для .net - http://www.springframework.net/ –

+1

Что делать, если мне нужно протестировать, несколько функций транзакции , как я знаю, что они работают, если я не могу совершить? –

+0

К dynmack.com: Да, к сожалению, это правильный момент. – nightcoder

0

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

Возможно, вы захотите, чтобы база данных была чем-то, что может быть установлено в программе, тогда ваш тест может сказать классам изменить базу данных.

3

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

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

+0

Как вы знаете, что ваши изменения работали, если вы не совершаете транзакцию? –

+0

@Robert - после выполнения обновления (но перед откатом) ваш тест будет выполнять все проверки, необходимые для проверки правильности обновления. Поскольку проверки выполняются в той же транзакции, что и обновление, они должны видеть обновления, даже если они еще не были зафиксированы. –

+0

Для вашей идеи транзакция должна быть в тесте, а не в методе, который вы тестируете. –

0

Этот код удаляет все данные из таблиц всех пользователей в MS SQL Server:

private DateTime _timeout; 

public void ClearDatabase(SqlConnection connection) 
{ 
    _timeout = DateTime.Now + TimeSpan.FromSeconds(30); 
    do 
    { 
     SqlCommand command = connection.CreateCommand(); 
     command.CommandText = "exec sp_MSforeachtable 'DELETE FROM ?'"; 
     try 
     { 
      command.ExecuteNonQuery(); 
      return; 
     } 
     catch (SqlException) 
     { 
     } 
    } while (!TimeOut()); 

    if (TimeOut()) 
     Assert.Fail("Fail to clear DB"); 
} 

private bool TimeOut() 
{ 
    return DateTime.Now > _timeout; 
} 
+0

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

1

Mocking это причины лучший способ проверить блок кода.

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

Я использую локальный экземпляр MySql для интеграционных тестов в нескольких проектах. Проблема возврата - это настройка сервера и создание тестовых данных. Я создал небольшой пакет Nuget под названием Mysql.Server (см. Больше на https://github.com/stumpdk/MySql.Server), который просто устанавливает локальный экземпляр MySql каждый раз, когда вы запускаете свои тесты.

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