Глава 8: Тестирование и устранение неполадок

В общем, вам следует использовать разработку через тестирование, особенно для приложений значительного размера. Это помогает сделать вашу программу более надежной и бездефектной. При почти 100-процентном тестовом покрытии TDD - практически единственный способ гарантировать правильность программы. Это особенно важно, учитывая, что Smalltalk типизируется динамически, а не статически. Статическая типизация призвана помочь в безопасности программирования. Однако использование TDD может полностью нивелировать это преимущество.

Поскольку Cranky - очень маленькое и простое приложение, в TDD нет необходимости. Тем не менее, здесь рекомендуется использовать его. Я показываю вам пример с классом #FFICranky.

TestCase subclass: #FFICrankyTest
   instanceVariableNames: ''
   classVariableNames: ''
   package: 'Cranky'
FFICrankyTest»testGetSysname
   self assert: FFICranky uniqueInstance getSysname equals: 'Linux'
FFICrankyTest»testGetCpuTemp
   self assert: FFICranky uniqueInstance getCpuTemp > 5.0

Обратите внимание: если температура процессора действительно ниже 5,0 ° по Цельсию, вы должны работать в морозильной камере!

Инструменты отладки

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

Двумя наиболее распространенными и эффективными инструментами для отладки являются оператор печати и утилита отладчика. Я предпочитаю оператор print, потому что он доступен для всех языков программирования и очень и очень прост в использовании. Для правильного использования отладчика требуется некоторое обучение.

Хотя отладчик Smalltalk несложен в использовании, демонстрация того, как его использовать, выходит за рамки данного руководства. Но вы все равно можете использовать старый добрый оператор печати! Используйте его, чтобы распечатать значения переменных. Используйте его для отслеживания пути выполнения. Таким образом вы сможете лучше понять, что происходит в вашей программе.

[Сейчас есть отдельное руководство Как использовать Pharo Debugger.]

Стресс-тестирование

Вы всегда должны проводить стресс-тестирование своего приложения. Запускайте его в самых разных условиях, какие только можете себе представить. Запустите его в течение длительного времени, даже на ночь. Запустите его несколько раз, закрыв и перезапустив приложение. Попробуйте все комбинации пользовательского ввода (это не относится к Cranky, поскольку у него нет пользовательского интерфейса или пользовательского интерфейса). Запустите его при большой загрузке процессора. И так далее.

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

Момент обучения

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

Исследование проблемы показало, что я не мог использовать отдельный поток, потому что Morphic не был «потокобезопасным» (что означает, что он не мог гарантировать безопасность, если к нему обращались более чем один поток одновременно).

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

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

Для этой конкретной ошибки я упростил следующий код в методе Cranky #initialize:

Cranky»initialize
   f := Form fromFileNamed: 'hot_air_balloon_mysticmorning.jpg'.
   m := ImageMorph new.
   m form: f.
   m openInWindowLabeled: 'Cranky'.
   delay := Delay forSeconds: 5.
   [ [ true ] whileTrue: [ delay wait ] fork

Таким образом, когда я запускаю Cranky, все, что я получаю, - это ImageMorph, отображаемый в окне, и поток, не делающий ничего, выполняющий бесконечный цикл с 5-секундной задержкой. В этом случае Cranky не аварийно завершает работу. (Я проводил тест примерно 20–30 раз. Если к тому времени он не прошел, значит, вероятно хорошо.)

Однако, если в потоке я просто заменяю информационное поле безобидной строкой, Cranky показывает периодические сбои:

"Create an instance variable 'a' first"
Cranky»initA
   a := ((StringMorph contents: '####') 
      color: Color white) position: 0@0.
   m addMorph: a
Cranky»initialize
   f := Form fromFileNamed: 'hot_air_balloon_mysticmorning.jpg'.
   m := ImageMorph new.
   m form: f.
   self initA.
   m openInWindowLabeled: 'Cranky'.
   delay := Delay forSeconds: 5.
   [ [ true ] whileTrue: [ a contents: '0'. delay wait ] fork

Ключевым моментом здесь является то, что этот сценарий настолько прост, что ничего не должно пойти не так. Но что-то есть! Ясно, что мой бесконечный цикл и Morphic не смешиваются.