2.4.3. Прическа кода

Назад Содержание Дальше

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

Итак, приступим. Рассмотрим последний написанный нами шаг детальнее. Как видно из последнего листинга, все элементы, с которыми происходит работа в данном шаге, находятся внутри окна dCheckBox. Это приводит нас к необходимости постоянно вписывать это название в коде, что приводит к многочисленному Ctrl-C , Ctrl-V. А это со временем надоедает. В данном случае это не столь тяжело воспринимается, но все-таки, а если мы будем работать, например, с окном, которое именуется как wTestApplicationWindow.dAdditionalParameters? Это просто пример длинного названия окна. Теперь представьте, насколько такое длинное название забивает код? Если код большой и достаточно сложный, то вот таки префиксы усложняют его читаемость просто одним своим видом. Но и убирать этот префикс вроде бы нельзя, все-таки это идентификаторы окна. Конечно, эту проблему можно решить строкой вида:

Code

	[ ] WINDOW wWin = wTestApplicationWindow.dAdditionalParameters
	[ ] wWin.edtSomeTextField.sValue = "Text"

Запись действительно сократится и текст будет читаться. Но появляются недостатки. Во-первых, не работает автозаполнение. Тип WINDOW соответствует любому объекту, который описан через оконный класс (MainWin, ChildWin, TextField, CheckBox и т.д. ) и неизвестно, в каком качестве такой объект используется ( стоит напомнить, что ни один компилятор еще не оснащен телепатическим анализатором и мысли разработчика читать не в состоянии). Во-вторых, это просто некорректно присваивать переменной объект, который динамически не меняется и уже описан так, что его можно использовать напрямую. Иными словами, не имеет смысл описывать тот объект, которые уже описан. Переменные типа WINDOW имеет смысл использовать для динамической работы с объектами, то есть:

  • Для выполнения однотипных действий с различными окнами
  • Для работы с объектами, которые не описаны во фрейме ( как правило, это объекты, наличие которых зависит от определенных выполненых действий ). Например, у нас есть веб-приложение, в котором используются динамические таблицы и в каждой строчке имеется некоторая кнопка, позволяющая сделать определенные операции с некоторой записью. Количество таких кнопок зависит от размеров таблицы. Поэтому не имеет смысл их всех загонять во фрейм, так как их количество может варьироваться.

В-третьих, теряется иерархия объектов. Может разработчику скрипта и легко можно будет найти окно, которому соответствует та или иная переменная, но если за поддержку скрипта возьмется другой человек ( а может и сам разработчик, но только через год – полтора), то ему такое соответствие найти будет гараздо труднее. А четко прослеживаемая иерархия позволяет сузить круг поиска в тех ситуациях, когда какой-либо элемент управления поменялся и нужно отыскать место, где произвести корректировку фрейма.

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

В целях уменьшения вложенности вызова объектов и просто для сокращения повторяющихся элементов используется блок with <window>. То есть мы определяем, с каким окном мы будем работать в пределах данного блока. В следующем примере мы модифицировали код последнего написанного шага, используя данный оператор.

Code

		[ ] wTestApp.Control.CheckBox.Pick()
		[+] with dCheckBox
			[-] if( !.Exists() )
				[ ] LogError("Check box window didn't appear")
				[ ] return
			[ ] 
			[ ] .cbTheCheckBox.SetState(FALSE)
			[ ] .cbTheCheckBox.Click()
			............................................................
			[ ] .btnExit.Click()
			[-] if( .Exists() )
				[ ] LogError("Check box dialog wasn't closed")

Как видно из примера, dCheckBox мы используем только в самом начале. Все последующие обращения к объектам данного окна внутри данного блока осуществляются через оператор . (точка). В примере, в блоке [+] with dCheckBox запись [-] if( !.Exists() ) равносильна [-] if( !dCheckBox.Exists() ). При отсутствии объекта слева от точки, автоматически подставляется окно with-блока. Таким образом, мы уменьшили вложенность обращения к объектам, локализовали работу с конкретным окном.

Следующим шагом корректировки кода можно сделать внедрение property в код. То есть, если нужно извлечь/установить некоторое состояние окна или элемента управления, то для этих целей лучше использовать именно property. Это и уменьшает код и более-менее соответствует смыслу данных обьектов. Теперь имеет смысл определить, что же именно реализовать через property. В первую очередь, это объекты классов TextField, CheckBox, PopupList, ComboBox, ListBox. У этих объектов легко определить, какая величина у них может быть на входе и какая на выходе. В рассматриваемом участке имеется строка .cbTheCheckBox.SetState(FALSE), которую как раз можно реализовать через property. Конечно, можно воспользоваться уже имеющимися средствами и заменить данную строку на .cbTheCheckBox.bValue = FALSE. bValue – стандартное свойство объектов класса CheckBox. Можно пойти дальше. Если данный элемент управления используется во многих тесткейсах или вообще не единожды, то имеет смысл дописать свойство, которое находится на одном уровне вложенности с соответствующим элементом управления. Это уменьшит вложенность обращений и сократит запись. В нашем случае cbTheCheckBox находится в окне dCheckBox. Для этого окна во фрейме допишем свойство bTheCheckBox. Аналогично можно дописать свойство, соответствующее текстовому полю ввода Label. В итоге декларация окна dCheckBox примет вид:

Code

	[+] window TestApp_Controls dCheckBox
		[ ] tag "Check Box"
		........................................................................................
		[ ] 
		[+] property bTheCheckBox
			[+] BOOLEAN Get()
				[ ] return this.cbTheCheckBox.bValue
			[+] VOID Set(BOOLEAN bValue)
				[ ] this.cbTheCheckBox.bValue = bValue
		[+] property sLabel
			[+] STRING Get()
				[ ] return this.edtLabel.sValue
			[+] VOID Set(STRING sValue)
				[ ] this.edtLabel.sValue = sValue

Исходя из этих изменений, строка .cbTheCheckBox.bValue = FALSE будет идентична строке .bTheCheckBox = FALSE.
Некоторым стандартным функциям соответствуют стандартные свойства. В качестве примера можно привести методы Exists, IsActive, IsEnabled, которым соответствуют свойства bExists, bActive, bEnabled. С методом Exists не все так просто, конечно, так как свойству bExists этот метод соответствует только если вызывать его без параметров, поэтому вызовы Exists следует заменять на свойства только если эти вызовы осуществляются буз параметров. Применив все свойства, мы получим следующий код:

Code

		[ ] wTestApp.Control.CheckBox.Pick()
		[-] with dCheckBox
			[+] if( !.bExists )
				[ ] LogError("Check box window didn't appear")
				[ ] return
			[ ] 
			[ ] .bTheCheckBox = FALSE
			[ ] .cbTheCheckBox.Click()
			[+] if( !.bTheCheckBox )
				[ ] LogError("""The Check box"" field isn't checked")
			[ ] 
			[ ] .cbEnabled.Click()
			[+] if( .cbTheCheckBox.bEnabled )
				[ ] LogError("""The Check box"" field is still enabled")
			[ ] 
			[ ] .cbEnabled.Click()
			[+] if( !.cbTheCheckBox.bEnabled )
				[ ] LogError("""The Check box"" field is not enabled")
				[ ] dCheckBox.cbEnabled.bValue = TRUE
			[ ] 
			[ ] sValue = "New Label"
			[ ] .sLabel = sValue
			[ ] .btnSetLabel.Click() 
			[+] if( .cbTheCheckBox.sCaption != (sValue) )
				[ ] LogError("Check box with recently entered label ""{sValue}"" wasn't found. Currently field ""{.cbTheCheckBox.sCaption}"" available")
			[ ] 
			[ ] .btnExit.Click()
			[+] if( .bExists )
				[ ] LogError("Check box dialog wasn't closed")

Красным помечены те участки, которые были заменены.

При корректировке кода также не следует забывать об ограничениях на длину строки кода. У многих команд разработчиков максимальная длина строки кода включена в список стандартов написания програмного кода (своеобразные ГОСТ-ы). В предыдущем примере специально была оставлена длинная строка:

Code

			[+] if( .cbTheCheckBox.sCaption != (sValue) )
				[ ] LogError("Check box with recently entered label ""{sValue}"" wasn't found. Currently field ""{.cbTheCheckBox.sCaption}"" available")

Такие строки без внимания оставлять нежелательно, так как длинные строки требуют прокрутки, чтобы просмотреть её целиком, что не очень удобно. Более того, если в строке еще используются какие-то выражения, то есть вероятность того, что это выражение в какой-то момент может выполнить недопустимую операцию, что повлечет генерацию исключения. И ошибку в такой строке труднее искать. Поэтому нужно ограничить максимальный размер строки и сделать перенос участка строки, который не поместился в заданные размеры. Итак, рассматриваемая строка имеет длину 131 символ и уже, например, не помещается целиком на экране с разрешением 1024х768 при стандартных размерах шрифта. Нужно как-то разбить эту строку на части. Предположим, что у нас ограничение на длину строки установлено в значение 100 символов. К счастью для строк определен оператор конкатенации “+”. Таким образом строка уже может быть представлена в виде:
“Check box with recently entered label “”{sValue}”” wasn’t found. “ + “Currently field “”{.cbTheCheckBox.sCaption}”” available”
В данном случае строку разбили по предложениям, как составным частям данного текста. Теперь нужно вставить вертикальную табуляцию. Для этого используется комбинация клавиш Shift-Enter. Устанавливаем курсор за первой строкой и жмем данную комбинацию клавиш. В результате часть строки, которая была справа от курсора, перенесется на следующую строку. Причем особенность такого переноса такова, то данная перенесенная строка будет рассматриваться как новая строка файла, но при этом эта строка воспринимается как продолжение предыдущей строки кода. Каждая строка кода начинается с [ ]. То есть запись:

Code

			[+] if( .cbTheCheckBox.sCaption != (sValue) )
				[ ] LogError("Check box with recently entered label ""{sValue}"" wasn't found. " +
                                                                         "Currently field ""{.cbTheCheckBox.sCaption}"" available")

Аналогична предыдущей записи. При этом строки файла не превышают максимальное допустимое значение. Читать данный текст удобнее, так как не надо делать лишнюю прокрутку.

 

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

Code

		[ ] LIST OF STRING lsMonths = { "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December"  }

Как видно, запись достаточно длинная. Здесь еще удлиняют строку пробелы, которые стоят вокруг запятых ( это сделано для того, чтобы запятые не потерялись среди кавычек ). Безусловно здесь можно применить Shift-Enter и получить нечто вида:

Code

		[ ] LIST OF STRING lsMonths = { "January" , 
                                "February" , 
                                "March" , 
                                "April" , 
                                "May" , 
                                "June" , 
                                "July" , 
                                "August" , 
                                "September" , 
                                "October" , 
                                "November" , 
                                "December"  }

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

Code

		[+] LIST OF STRING lsMonths = {...}
			[ ] "January"
			[ ] "February"  
			[ ] "March" 
			[ ] "April" 
			[ ] "May"
			[ ] "June"
			[ ] "July"
			[ ] "August"
			[ ] "September"
			[ ] "October"
			[ ] "November"
			[ ] "December"

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

Code

		[+] LIST OF STRING lsMonths = <text>
			[ ] January
			[ ] February
			[ ] March
			[ ] April
			[ ] May
			[ ] June
			[ ] July
			[ ] August
			[ ] September
			[ ] October
			[ ] November
			[ ] December

 

Это основные моменты, которые желательно учитывать, так как они заметно упрощают написание/чтение програмного кода. Остальные моменты, которые будут только перечислены, учитывать необязательно. Это просто один из вариантов стандартов написания кода, которого можно придерживаться. Итак, некоторые дополнительные модификации с целью улучшения читаемости кода:

  • Локальные переменные и константы в функциях/тесткейсах объявляются в самом начале в отведенном для этого месте.
  • Имена переменных начинаются с префикса, соответствующего типу данной переменной
  • В конструкциях if, for, while выражения в скобках отделяются от скобок пробелами
  • В выражениях каждый оператор отделяется от операндов пробельным символом

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


Назад Содержание Дальше