Как правильно писать тесты 46 Цикл разработки 46 Структура проекта с тестами 51 Утверждения (Asserts) 52 Утверждения в форме ограничений 54 Категории 56

Вид материалаТесты

Содержание


Общеязыковая спецификация
Модульное тестирование (unit testing) Предпосылки
Делает ли мой код именно то, для чего он предназначен?
Можно ли рассчитывать, что написанный код не подведет при развитии проекта?
Поощрение изменений
Упрощение интеграции
Документирование кода
Отделение интерфейса от реализации
Подобный материал:
1   ...   7   8   9   10   11   12   13   14   ...   47

Общеязыковая спецификация


Технология СОМ позволяет взаимодействовать объектам, созданным на разных языках. С другой стороны, CLR обеспечивает интеграцию языков и позволяет объектам, созданным на одном языке, быть «равноправными гражданами» кода, написанного на другом. Такая интеграция возможна благодаря стандартному набору типов, информации, описывающей тип (метаданным), и общей среде выполнения CLR.

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

Чтобы создать тип, доступный из других языков, придется задействовать лишь те возможности языка, которые гарантированно доступны в других. Чтобы помочь в этом, Microsoft определила общеязыковую спецификацию (Common Language Specification, CLS), описывающую минимальный набор возможностей, который должны реализовать производители компиляторов, чтобы их продукты работали в CLR.

CLR/CTS поддерживают гораздо больше возможностей в сравнении с подмножеством, определенным в CLS, так что, если вас не волнует межъязыковое взаимодействие, можете разрабатывать очень мощные типы, ограничиваясь лишь возможностями языка. В частности, CLS определяет правила, которым должны соответствовать видимые извне типы, чтобы к ним можно было получить доступ из любых других CLS-совместимых языков программирования. Заметьте: правила CLS не применяются к коду, доступному только из сборки, в которой он содержится. Эти принципы обобщены на рис.:




Как видите, CLR/CTS предоставляет определенный выбор. Программист может решить использовать ассемблер IL, и тогда ему будут доступны все функции CLR/ CTS. Большинство других языков, таких как С#, Visual Basic и Fortran, предоставляет подмножество возможностей CLR/CTS. Минимальный набор функций, которые должны поддерживать все языки, определяется CLS.

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


Сформулируем саму суть правил CLS. В CLR каждый член типа — либо поле (данные), либо метод (действие). Это значит, что каждый язык должен «уметь» предоставлять доступ к полям и вызывать методы. Некоторые поля и методы используются особыми и стандартными способами. Чтобы упростить программирование, языки обычно предоставляют дополнительный уровень абстракции. Например, применяются такие конструкции, как перечисления, массивы, свойства, индексаторы, делегаты, события, конструкторы, деструкторы, перегрузка операторов, операции преобразования и т. д. Встречая их в исходном коде, компилятор должен перевести их в поля и методы, чтобы они были доступны CLR и другим языкам.


Модульное тестирование (unit testing)8

Предпосылки




В любой большой, серьёзной программе, с большим количеством функциональности довольно сложно искать ошибки «в лоб». Иногда, чтобы добиться того, чтобы только что написанный участок кода сработал, необходимо сделать довольно много действий в программе: скомпилировать и собрать весь проект, создать тестовую базу данных, сконфигурировать программу, создать необходимое окружение, выполнить цепочку других необходимых действий... А для полноценной проверки вновь написанного кода может потребоваться проделать эти действия несколько раз с небольшими вариациями. Всё это может занять в несколько раз больше времени, чем собственно написание кода.


Поэтому опытные программисты применяют давно испытанный приём «Разделяй и властвуй». Они проверяют работоспособность каждого отдельного небольшого модуля. Если все модули по отдельности работают как надо, то проверяется работоспособность интеграции этих модулей друг с другом. Этот подход носит название модульное тестирование (Unit testing).


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


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


Преимущества




Цель юнит-тестирования — изолировать отдельные части программы и показать, что по отдельности эти части работоспособны.


Этот тип тестирования обычно выполняется программистами.


Основные вопросы, на которые помогают ответить юнит-тесты.


Делает ли мой код именно то, для чего он предназначен?


Делает ли мой код именно то, для чего он предназначен при любых условиях?

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


Можно ли рассчитывать, что написанный код не подведет при развитии проекта?

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


Поощрение изменений


Юнит-тестирование позже позволяет программистам проводить рефакторинг9, будучи уверенными, что модуль по-прежнему работает корректно. Эти проверки называются регрессионным тестированием. Такая уверенность поощряет программистов к изменениям кода, поскольку достаточно легко проверить, что код работает и после изменений.

Упрощение интеграции


Юнит-тестирование помогает устранить сомнения по поводу отдельных модулей и может быть использовано для подхода к тестированию «снизу вверх»: сначала тестируются отдельные части программы, затем программа в целом.

Документирование кода


Юнит-тесты можно рассматривать как «живой документ» для тестируемого класса. Клиенты, которые не знают, как использовать данный класс, могут использовать юнит-тест в качестве примера.


Отделение интерфейса от реализации


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