Хочу разобраться с ошибкой выполнения теста, которая вываливается без видимых причин по вине, как мне кажется QTP
Дано приложение, написанное на Delphi и изобилующее нестандартными контролами. Для их определения были написаны xml файлы extensiblity и некоторая добавка, которая вкомпилируется в само приложение.
QTP версии 10, до этого был 9.5, Windows XP со всеми апдейтами и прочее. Скажу сразу, обновился сегодня до 11 версии QTP, проблема пока не повторилась, но вдруг.
Тест падает на последней строке с кодом -214746725 "General run-time error" и деталями "Неопознанная ошибка". Подробности ниже.
Итак, запуск, логин на сервер, выход. Первая часть, запуск, выглядит так:
Скрытый текст
Public Sub LaunchMicex()
SystemUtil.CloseProcessByName(AppName) ' тут убивается процесс приложения, а не просто мягкий выход
SystemUtil.Run(appFullPath)
ReportPass "Запуск приложения выполнен", "Приложение запущено по пути: " & appFullPath
End Sub
и отрабатывает независимо ни от чего.
Вторая часть заполняет диалоговое окошко логина, там тоже нет проблем, все контролы там более-менее стандартные (или был маппинг к стандартным), с третьей частью проблемы.
Скрытый текст
Public Function InvokeForm(menuPath)
InvokeForm = True
On Error Resume Next
Dim targetMenuPath, obj
targetMenuPath = menuPath
Set obj = GetTOByName(FORM_MAIN, GetID(MAIN_MENU), toClass)
obj.Select targetMenuPath ' <<<==== ТУТ появляется ошибка!
If err.Number <> 0 Then
Set ec = GetErrorCopy()
On Error GoTo 0
Err.Raise ec.number, signature, FormatError(ec, "Не удалось обратиться к меню " & targetMenuPath & "]")
End If
On Error GoTo 0
End Function
GetTOByName:
Private Function GetTOByName(ByVal formName, ByVal toName, ByRef toClass)
Dim toNode
On Error Resume Next
' поиск в репозитории подходящий объектов, подходящий объект должен быть один.
Set buf = GetFormNode(formName).ChildElementsByPath("./qtpRep:ChildObjects/qtpRep:Object[@Name='" & toName & "']")
If Err.Number<>0 Then
On Error GoTo 0
Err.Raise ec.number, signature, FormatError(ec, "Окно " & formName & " не найдено в репозитории")
ElseIf buf.Count = 0 Then
On Error GoTo 0
Err.Raise ERROR_GENERAL, signature, "Объект " & formName & "." & toName & " не найден в репозитории"
ElseIf buf.count > 1 Then
On Error GoTo 0
Err.Raise ERROR_GENERAL, signature, "Найдено несколько объектов с именем " & formName & "." & toName & " в репозитории"
End If
On Error GoTo 0
Set toNode = buf.Item(1)
' test object name and class are attributes of TO node
toClass = toNode.Attributes.ItemByName("Class").Value
On Error Resume Next
Select Case toClass
' тут долгое перечисление того, какие типы знаю
Case "DelphiTBXToolbar"
Set GetTOByName = GetFormByName(formName).DelphiTBXToolbar(toName)
' GetFormByName выдаёт из репозитория объект, соответствующий имени formName
Case Else
On Error GoTo 0
Err.Raise ERROR_GENERAL, signature, "Неизвестный класс " & toClass & " тестового объекта " & formName & "." & toName
End Select
If err.Number<>0 Then
Set ec = GetErrorCopy()
On Error GoTo 0
Err.Raise ec.number, signature, FormatError(ec, "Ошибка получения тестового объекта " & formName & "." & toName)
End If
On Error GoTo 0
End Function
При обращении к меню в первый раз всё происходит хорошо. При обращении второй раз тест вываливается с ошибкой на строке obj.Select targetMenuPath.
Extensibility:
Скрытый текст
<ClassInfo Name="DelphiTBXToolbar" BaseClassInfoName="DelphiObject" GenericTypeID="object">
<TypeInfo>
<Operation Name="Select" PropertyType="Method">
<!-- Appears as tooltip in Keyword View -->
<Description>Select toolbar item by ItemName.</Description>
<!-- Used to generate the 'Documentation' column in Keyword View -->
<Documentation><![CDATA[Select : %a1.]]></Documentation>
<!-- definition of the MenuPath argument -->
<Argument Name="MenuPath" Direction="In" IsMandatory="true">
<Type VariantType="String"/>
</Argument>
</Operation>
</TypeInfo>
<!-- Identification property definitions.
By default all identification properties are displayed in the Object Spy.
Identification properties for which ForDescription="true" comprise the
test object description. QuickTest uses these properties to
identify the correct object in the application.
Identification properties for which ForVerification="true", can be accessed
in QuickTest by checkpoints and output values. -->
<IdentificationProperties>
<IdentificationProperty Name="delphi_name" ForDescription="true"/>
<IdentificationProperty Name="y" ForVerification="true"/>
<IdentificationProperty Name="x" ForVerification="true"/>
<IdentificationProperty Name="WindowExtendedStyle" />
<IdentificationProperty Name="WindowStyle" />
<IdentificationProperty Name="width" ForVerification="true"/>
<IdentificationProperty Name="visible" ForVerification="true"/>
<IdentificationProperty Name="RegExpWndTitle" />
<IdentificationProperty Name="RegExpWndClass" />
<IdentificationProperty Name="NativeClass" ForDescription="true"/>
<IdentificationProperty Name="hWnd" />
<IdentificationProperty Name="height" ForVerification="true"/>
<IdentificationProperty Name="focused" ForVerification="true"/>
<IdentificationProperty Name="enabled" ForVerification="true"/>
<IdentificationProperty Name="abs_y" />
<IdentificationProperty Name="abs_x" />
<IdentificationProperty Name="Position" ForVerification="true"/>
</IdentificationProperties>
</ClassInfo>
Могу ещё привести код на Delphi, который в приложение встраивается, но пока не хочу. Этот модуль писал не я, он три года работает.. Хотя там тоже есть странности.
У меня есть подозрение, что падает, потому что QTP пытается работать с несуществующим объектом:
когда мы работаем в первый раз, то создается привязка тестового объекта QTP к объекту внутри приложения.
Когда мы пытаемся работать с тем же объектом после перезапуска приложения в контексте одного и того же теста может получиться так, что мы не заново ищем объект в системе, а берем тот же самый тестовый объект внутри QTP, который был создан в первый раз. Но этот тестовый объект указывает на уже несуществующий объект внутри приложения (поскольку мы приложение перезапустили, и там объекты пересоздались). Отсюда и возникает ошибка, которую вы видите.
11-я версия QTP может внутри себя запускать метод init для тестовых объектов, который заново перепривязывает существующий тестовый объект к объекту внутри приложения, поэтому на новой версии работает, а на старой нет.
Но завтра постараюсь посмотреть более точно.
Кстати, для проверки догадки, можно попробовать вставить вызов obj.init перед obj.Select. Если после этого будет нормально работать, значит проблема в буферизации объектов. Если нет, то проблема все равно может быть в буферизации, поскольку я не знаю, является ли метод init открытым для тестовых объектов Delphi.