Могу ли я создать тест на взаимоблокировку базы данных в Nunit?

#sql-server-2000 #nunit #deadlock #msdtc

Вопрос:

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

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

Как я могу настроить и запустить тест «взаимоблокировки» с использованием TransactionScope и SQL2000 (т. Е. задействован MSDTC), который может быть надежно воспроизведен? Более подробно: я знаю, что существует ситуация, когда, если два пользователя вызывают две функции с разными конкретными значениями данных, может возникнуть тупик. Как я могу смоделировать это в NUNIT — и сделать так, чтобы тупик всегда происходил?

И да, я начал с плана действий «Почему бы вам не остановить возникновение тупиков в первую очередь», но у меня нет контроля над кодом, в котором могут возникать тупики — я просто вызываю функции, и они могут блокироваться.

Ответ №1:

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

Основная идея заключается в том, что вы говорите своей структуре макетных объектов (мне нравится TypeMock) вместо этого выдавать исключение, что-то вроде этого:

 MockObject mo = MockManager.MockObject(typeof(MyDeadlockException));
mock.ExpectAndThrow("MyMethod", (MyDeadlockException)mo.Object); 
 

Идея в основном та же самая для других насмешливых фреймворков.

Комментарии:

1. Мне нравится эта идея. У меня есть представление о макетных фреймворках, но я никогда их не использовал. Я предполагаю, что метод «ExpectAndThrow» на самом деле вызовет исключение, поэтому мой код может его перехватить. Я разберусь в этом подробнее.

2. Это именно то, что он делает. Я им не пользовался, но если у тебя проблемы с бюджетом, Носорог. Издевается бесплатно: ayende.com/projects/rhino-mocks.aspx

Ответ №2:

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

Закройте эти лазейки — Воспроизведите Ошибки базы данных

Автор — Алексей Кузнецов.

Комментарии:

1. Я не уверен, как использование пользовательского класса блокировки и теста, вызывающего класс блокировки, может помочь мне проверить наличие блокировок в конкретном вызове моего существующего asp.net приложение? Хотя статья интересная.

Ответ №3:

Что, если один из ваших тестов в середине транзакции просто «подождет» около 5 минут? Или вы просто пишете тест, который запускает транзакцию, создает новую запись, а затем обновляет эту запись без фиксации. Затем запустите новую транзакцию и попробуйте прочитать ту запись, которая была создана и в настоящее время обновляется. Там вы зайдете в тупик.

Комментарии:

1. Создание двух транзакций в одном потоке не может привести к взаимоблокировке с помощью TransactionScope. Если я создам один txn, а затем создам другой, не фиксируя первый, второй просто станет вложенным в первый…..

Ответ №4:

Что делать, если вы вручную заблокировали таблицу и ВСЕГДА оставляли ее заблокированной? Тогда любые действия, которые вы предприняли против этого стола, приведут к тупиковой ситуации?

Комментарии:

1. «Вручную» создать транзакцию с помощью MSDTC непросто. Конечно, я могу начать транзакцию в анализаторе запросов, но это не распределенная транзакция, и все, что происходит,-это то, что она блокирует транзакцию MSDTC от выполнения какой-либо работы с базой данных до тех пор, пока я не «Откат» в анализаторе запросов.

Ответ №5:

Подходя к этому вслепую, но возможно ли в вашем методе TestSetup фактически создать соединение SqlConnection с вашей базой данных? Затем, используя это, вы можете просто выдать команду для блокировки таблицы или предпринять какие-либо действия для блокировки записи или страницы? Таким образом, это будет вне любых других транзакций, которые у вас есть? Похоже, что это был бы вариант, который вы уже рассматривали. Что я упускаю из виду в вашей ситуации?

Комментарии:

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

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

Ответ №6:

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

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

В общем случае модульный тест должен выполняться только на тестируемом коде и не полагаться на какой-либо другой код или компоненты. Тем не менее, базы данных — это, по сути, еще один компонент, и вы, вероятно, проводите какие-то функциональные тесты, используя nunit для их управления.

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

Комментарии:

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

2. Я знаю, что мы можем получить тупики, потому что в необработанном журнале ошибок текущей версии приложения live они есть 🙂 Я понимаю, что тесты Unittest обычно не должны проверять БД, но я настроил базу данных unittest специально для тестирования, которая не изменяет состояние.