- PVSM.RU - https://www.pvsm.ru -

Модульного тестирования недостаточно. Нужна статическая типизация!

Когда я работал над своей магистерской диссертацией, я пообещал себе, что опубликую ее в Интернете под свободной лицензией. Я получил степень, но, к сожалению, натолкнулся на одно из неписаных правил ВУЗов — когда вы тратите на интересующий вопрос много времени, он вам надоедает.
Наконец, спустя год, я все-таки ее публикую.
Для тех, кому лень знакомиться с моим полным трудом (в любом случае, 60 страниц текста это не так много для исследовательской работы, но это все же не мало), я предлагаю краткую версию статьи. Прошу заметить, что краткая версия не учитывает некоторые важные сведения, поэтому я прошу писать отзывы только о полной версии.

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

Суть их рассуждений в следующем:

  1. Статической типизации недостаточно для выявления багов, поэтому необходимы модульные тесты;
  2. Статическая типизация становится лишней, так как у вас есть тесты;
  3. Из-за статической типизации некоторые корректные программы могут выдавать предупреждения на стадии компиляции.

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


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

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

Критерии для динамического языка были следующими:

  1. этот язык должен быть динамически типизированным;
  2. язык должен поддерживать модульное тестирование;
  3. необходимо достаточное количество opensource-решений на этом языке;
  4. язык должен быть известным и иметь достаточное количество сторонников.

Под эти критерии отлично подходит Python.

Дальше предстояло выбрать статический язык, учитывающий несколько требований:

  1. язык должен выполняться на тех же платформах, что и Python;
  2. язык должен иметь строгую типизацию;
  3. язык должен пользоваться уважением сторонниками статической типизации.

Haskell удовлетворял всем этим требованиям.

Далее я выбрал четыре случайных проекта, которые предстояло транслировать с Python на Haskell: The Python NMEA Toolkit [1], MIDIUtil [2], GrapeFruit [3]и PyFontInfo [4].

The Python NMEA Toolkit

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

MIDIUtil

Трансляция MIDIUtil привела к выявлению двух ошибок. Одну из них можно было бы отследить с помощью теста. Еще одну устранила бы статическая типизация. Утилита MIDIUtil использует методы struct.pack и struct.unpack, которые не могут быть транслированы в язык Haskell напрямую, поскольку в них используется строка форматирования, содержащая типы аргументов и возвращаемого значения.
Однако во всех случаях использования, форматирующие строки были жестко закодированы, так что код на Haskell мог использовать вместо них жёстко кодированные функции, не теряя выразительности. Если бы в MIDIUtil такие строки хранились в конфигурационном файле, то для её трансляции в язык со статической типизацией пришлось бы серьёзно поработать над архитектурой решения.

GrapeFruit

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

PyFontInfo

Перевод из PyFontInfo привел к выявлению 6 ошибок типов. Две ошибки при исполнении можно было бы устранить при использовании статической типизации. От одного теста можно было бы избавиться. PyFontInfo тоже использует struct.pack и struct.unpack, поэтому не может быть переведен в Haskell, но это ограничение можно обойти.

Результаты

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

Заключение

Результаты эксперимента показывают, что модульное тестирование не может считаться заменой статической типизации и не учитывает все проблемы. Существенной проблемой является и то, что построение грамотного теста для типов трудоемко, а в языках со статической типизацией это делается автоматически. Применение статической проверки типов для программ, написанных на динамических языках, позволило бы выявить дефекты, которые не обнаруживаются тестами. И в то же время не требуют крупной переработки архитектуры.
Опыт показал новый взгляд на проблему избавления от ошибок типизации с помощью юнит-тестов. Надеюсь, что это кого-то заинтересует, и они проведут эксперимент на других проектах.
Полную версию статьи вы можете прочитать здесь [5].

Автор: Mobyman

Источник [6]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/42229

Ссылки в тексте:

[1] The Python NMEA Toolkit: https://python-gpsd.googlecode.com/

[2] MIDIUtil: https://code.google.com/p/midiutil/

[3] GrapeFruit : https://code.google.com/p/grapefruit/

[4] PyFontInfo: https://bitbucket.org/sirpengi/pyfontinfo

[5] здесь: https://docs.google.com/open?id=0B5C1aVVb3qRONVhiNDBiNUw0am8

[6] Источник: http://habrahabr.ru/post/192108/