четверг, 14 января 2010 г.

Как писать unit-тесты для тестирования DAL

Unit-тесты для тестирования DAL (которые в свою очередь чаще работают с наборами данных типа DataSet чем с ДатаРидерамиJ ) – применяются …. Редко. Чаще с помощью unit-тестов тестируются пригодные для тестирования классы бизнес-логики. Все остальное, в т.ч. DAL и даже пользовательский интерфейс протестировать можно, но требует как некоторых дополнительных усилий на стороне тестов, так и разработки с учетом возможности последующего Unit-тестирования. ( Testability).

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

В данном случае нужно:

Проводить тесты на предсказуемом наборе данных. Т.е. каждый [Setup] / [Teardown] база должна переходить в одно заранее известное состояние. Т.е содержать известный заранее набор данных.

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

Удобнее всего было бы написать что-то типа

DBAssert(“имяБазы.Табличка.Колонка”, “id = 2”, 1500); - т.е. воспользоваться готовым плагином к NUnit умеющим самостоятельно зачитывать данные из базы.

К сожалению я пока не слышала чтобы кто-то спортировал такую вещь и под .Net / NUnit. Можно посмотреть на www.nuinit.org / www.sf.net– если такая библиотечка уже доступна, соответственно используйте.

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

1. Использовать наши же методы, нашего же компонента которые возвращают значения из базы если такие есть. Плюсы – не надо писать дополнительного кода. Минусы – необходимо НЕЗАВИСИМО проверять каждый метод компонента. Иначе ошибка в методе вытаскивающем данные – повлияет на результаты проверки метода меняющего данные. Юнит тесты ( в идеале :) настраиваются выполняться автоматически, предположим каждую ночь, а результаты их работы (т.е найденные ошибки) получают все члены команды с утра( в идеале – девелопер ответственный за этот метод + Лид только). Потому ложные срабатывания unit-тестов это плохо. Кроме того, ошибка в методе получения информации может так (не)удачно дополнять ошибку в методе изменения, что вы получаете ‘положительный’ результат ‘проверки’ - что явно не есть хорошо.

2. Создать код в юнит-тесте (используя ADO.NET) который обращался бы в базу и сравнивал бы значения. Если он получится хорошим/надежным можно и стоит его выделить в библиотечку которую так всем не хватает. По сравнению с готовой ИСКОМОЙ библиотекой – ваша явно будет менее оттестирована. Т.е. сама utilitu библиотека может содержать ошибки, потому лучше найти готовую и поддерживаемую.

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

Можно посмотреть еще, что такое http://en.wikipedia.org/wiki/Mock_Object,

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

ЗЫ Unit-test это суть СКРИПТ понятный как кодеру теста так и разработчику теста. Разработчик теста НЕ обязан быть программистом. Его ключевое умение – определить необходимый и достаточный в данном случае набор тестов. Потому:

А) комментарии что же мы проверяем – над каждым тестом

Б) сам код теста – простая проверка с захардкоденными значениями, что-то из серии

Assert(MyComponent.Sum(2,2), 4) // Просто хардкоднная проверка что 2+2 = 4 .. без попытки написать что-то сложное

В) все проверки выполняются над системой в известном состоянии. Не следует предполагать что предыдущий тест перевел систему в нужное состояние. Надо гарантировать нахождение системы в подходящем состоянии ( [Setup] , [Teardown] )

Г) Ложное срабатывание теста это ПЛОХО. Как и ложное несрабатывание естественно ;-) Потому обратите внимание, красным должен быть тест если тестируемый метод содержит ошибку, но ЖЕЛТЫМ если тест просто не удалось провести.

Д) Исключения: Иногда с помощью unit-тест делают SmokeTest – суть которого хаотичный или лучше сложно навернутый перебор комбинаций данных, именно с целью найти ошибку случайными данными. Это отдельная задача.

Варианты:

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

Не факт. Если второй datreader гарантированно читает данные правильно ( например проверено применением некого гипотетического DBNetUnit ) сотнями разработчиков – то таким точным протестированным инструментом легко найти ошибки в компоненте. Если же такого инструмента нет – то все равно вероятность ошибки и в коде будет существенно меньше, чем только в коде. Но как Вы правильно заметили увеличение трудоемкости в двое – при не 100%-ном результате оправдано далеко не всегда. Но бывает и оправдано:

http://www.n-admin.com/n37-871.html

2.Забить в тест как бы наперед известные данные и сравнивать с возвращаемыми. Но тогда, если значения в базе изменятся, проверка не пройдет.

В базе должны быть точно известные значения. Тест происходит на эталонной базе. Если эталонная база содержит ерунду вместо ожидаемых тестов - проблемы специалиста по автоматизации и конфигурированию тестов. Разработчик тестов пишет их в уверенности в корректности базы – и этим не парится :)

Есть ли еще какие-нибудь варианты?

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

Отправить комментарий