Одной из самых больших проблем тестирования пользовательского интерфейса является его интерактивность. Часто для выполнения тестового сценария необходимо получить подтверждение пользователя на выполнение определенной операции.
Например:
(MessageBox confirm: 'Do you want to overwrite this file?') ifTrue: [...]
Традиционным способом решения этой проблемы является вынесение всех обращений к классу MessageBox в отдельный объект-адаптер с последующей его заменой на mock-object.
Но в Smalltalk возможен еще один более простой вариант благодаря наличию продолжаемых исключений (resumable exceptions).
Во-первых, сначала необходимо создать собственный подкласс класса Notification (это исключение по-умолчанию является продолжаемым):
Notification subclass: #ConfirmationNotification instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''
В коде везде, где требуется подтверждение, пишем следующий вызов:
(ConfirmationNotification signal: 'Do you want to overwrite this file?') ifTrue: [...]
Тогда тестирование сценария с подтверждением может быть осуществлено следующим образом:
testExample1 [ Тестовый вызов testedObject doSomething] on: ConfirmationNotification do: [:n | Эмулируем подтверждение n resume: true]. Поверяем, что действие выполнено
Это вариант с одним подтверждением. А что делать, если их может быть несколько по различным поводам? Первый способ, это использовать поле исключения tag, которое передается с помощью сообщения #signal:with:. Второй способ, это создавать различные подклассы ConfirmationNotification. Он более предпочтителен, когда определенные виды подтверждений встречаются регулярно.
С тестами разобрались, а как теперь сделать, чтобы реальное приложение показывало диалог в соответствующих местах?
Как правило, любое приложение имеет центральный обработчик исключений, отлавливающий ошибки в обработчиках событий верхнего уровня. Именно в него и следует внести поддержку подтверждений:
Object>>handleDomainErrors: aBlock
aBlock
on: Error do:
[:e | MessageBox errorMsg: e messageText caption: 'Error']
on: ConfirmationNotification do:
[:n | n resume: (MessageBox confirm: n messageText]
Далее нужно лишь убедиться, что все события верхнего уровня содержат вызов этого обработчика:
someTopLevelEventHandler self handleDomainErrors: [ Выполнить необходимые действия ]
P.S. Примеры кода приведены для Dolphin Smalltalk, поэтому обращение к классу MessageBox должно быть заменено на эквивалентное действие в других диалектах. Дополнительно, в VisualWorks Smalltalk сообщение #signal: следует заменить на #raiseSignal:.