Перейти к содержимому

edd1977

Регистрация: 18 мая 2018
Offline Активность: 30 мая 2018 14:06
-----

Мои темы

Свойство UItestControl.NativeElement - как его использовать?

24 мая 2018 - 14:59

Пишу тестовый проект для наших Silverlight-приложений (Silverlight 5, VS 2015). За основу взяты библиотеки семейства TestTools, в частности UITesting с ее UITestControl. Однако, зачастую в проектах использованы библиотеки DevExpress. Особенность элементов из них в том, что Coded UITest Builder часто вылетает при попытке добавить такой элемент в UI карту, иногда вылет происходит при обращении к соседнему ЭУ или при обходе стандартных ЭУ средствами навигации билдера, если в одном уровне с ними есть такой DevExpress элемент.

Однако часть этих проблемных контролов хорошо управляется через библиотеку UI Atomation. Но вот в чем проблема: по моему опыту, перейти из одной иерархии в другую (мне интересен переход от UITestControl к AutomationElement) легко можно только через AutomationElement.FromHandle(), причем Handle в дереве UITestContol-ов можно доступен в двух местах: UITestControl.Desctop и UITestControl.WindowHandle. На мой взгляд - это довольно далекие узлы иерархии. Получаем окно - это окно браузера и начинаем спускаться по AutomationId до нужного элемента. Хотелось бы что-то более однозначное.

 

У UITestControl есть свойство NativeElement. В случае UITestControl.TopParent (окно браузера) - это массив из двух элементов. Первый элемент можно привести к IAccessible, и это работает. Но это уж совсем старая технология, как я понял и она содержит наименьшее количество свойство и методов и подобных. Второй элемент число, как правило 0. Получить из этой связки AutomationElement у меня не получилось. Я пробовал по-разному, например: (win.NativeElement as object[])[0] as AutomationElement или AutomationElement.FromIAccessible(acc, 0), где acc - это приведенный к IAccessible 1-й элемент массива - не работает. Но по крайней мере, я смог получить объект из другого фреймворка, пускай и ненужного в данном случае.

 

Я решил попробовать то же самое для конкретного UITestControl - для SilverlightCheckBox. В результате NativeElement представлял собой массив уже из трех элементов: 2, 636627633619708604, "CheckBox". Здесь только простые типы и явно это уже не IAccessible элемент. Но я так же не смог получить AutomationElement из данного набора. Пробовал AutomationElement.FromHandle(<подставлял большое число и пытался из него получить IntPtr>) - не помогло, IntPrt из него не получается :)

 

Объясните пожалуйста, как правильно работать и для чего вообще нужно свойство NativeElement? Его физический смысл, откуда оно вообще взялось, и из чего состоит?

Можно ли как-то точно получить AutomationElement из UITestControl если NativeElement заполнено? 

 

 

PS

Пока писал, вспомнил про программный поиск ЭУ через FindMatchingControls или просто Find. Как там это работало - надо освежать в памяти, какие-то DX ЭУ можно было так получить, но все равно проблемы были. Вспомню, отпишусь об этом обязательно дальше в этой теме.


Как работают WaitForControlXXX методы в Coded UI тестах?

18 мая 2018 - 14:13

В данный момент оптимизирую свой тестовый проект для приложения на Silverlight 5. Использую классы из UITesting (в редких случаях из UI Automation). Как вариант оптимизации и повышения устойчивости, решил проставить методы вида WaitForControlXXX в необходимых на мой взгляд местах. И первое, с чем столкнулся - мое ожидание того как эти методы должны работать расходится с практическим результатом.

Поясню на примере: создал простой UserControl в Silverlight, который состоит из двух кнопок, одна из которых при запуске не видна. При нажатии на первую кнопку (First) становится видна вторая - Second. А при нажатии на Second программно добавляется третья кнопка - Third. Сделал сопутствующий CodedUITest-проект, в котором планировал проверить работу методов класса UITestControl: WaitForControlPropertyNotEqual (для кнопки Second) и WaitForControlExists - для кнопки Third.

 

Silverlight-контрол очень простой (развертывается в простом Web-проекте и запускается в браузере):

 

<Grid x:Name="LayoutRoot" Background="White">
        <Button HorizontalAlignment="Left" VerticalAlignment="Top"
                AutomationProperties.AutomationId="btnFirst"
                Click="Button_Click"
                Name="btnFirst"
                >First</Button>
        <Button HorizontalAlignment="Right" VerticalAlignment="Top"
                AutomationProperties.AutomationId="btnSecond"
                Click="Button_Click_1"
                Visibility="Collapsed"
                Name="btnSecond"
                >Second</Button>
    </Grid>
 
Процедура Button_Click делает: btnSecond.Visibility = Visibility.Visible; // делаем кнопку видимой
Процедура добавления 3-й кнопки (Button_Click_1), так же проста:
 
Button btn = new Button();
            btn.Name = "btnThird";
            btn.Content = "Third";
            btn.HorizontalAlignment = HorizontalAlignment.Left;
            btn.VerticalAlignment = VerticalAlignment.Bottom;
            //
            LayoutRoot.Children.Add(btn);
 
Теперь самое главное, что я ожидал и что я получил. Тестовый метод выглядит следующим образом:
 
WaitForXxxxMapClasses.WaitForXxxxMap uimap = new WaitForXxxxMapClasses.WaitForXxxxMap(); // моя UIMap, которую я получил с помощью UI test builder.
            //
            SilverlightButton first = uimap.UIWaitForXxxxTestingInWindow.UIWaitForXxxxTestingDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UIFirstButton;
            SilverlightButton second = uimap.UIWaitForXxxxTestingInWindow.UIWaitForXxxxTestingDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UISecondButton;
            SilverlightButton third = uimap.UIWaitForXxxxTestingInWindow.UIWaitForXxxxTestingDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UIThirdButton;
            //
            second.WaitForControlPropertyNotEqual(UITestControl.PropertyNames.Height, 0); // Исследуемая функция.
            //
            Rectangle rect = second.BoundingRectangle;
            Point center = new Point();
            center.X = rect.Left + rect.Width / 2;
            center.Y = rect.Top + rect.Height / 2;
            //
            Mouse.Click(center);
 
Я ожидал, что метод WaitForControlPropertyNotEqual для кнопки second должен блокировать ход выполнения тестового метода до тех пор, пока высота кнопки не станет больше 0 (т.е. кнопка станет видимой) или пока не кончится время по умолчанию. В MSDN написано так: Blocks the current thread until the specified property of this control is not equal to the specified value, or until the default time-out expires./
Конечно я проверил значение Height - оно равно 0 и параметры объекта Playback.PlaybackSettings, вот они на момент выполнения метода:
Test Name: Parameters
Test Outcome: Passed
Result StandardOutput:
DelayBetweenActions = 100
SearchTimeout = 120000
WaitForReadyTimeout = 60000
ShouldSearchFailFast = True
SmartMatchOptions = TopLevelWindow, Control
SearchInMinimizedWindows = True
ThinkTimeMultiplier = 1
ImeLanguageList = System.Collections.Generic.List`1[System.Int32]
MaximumRetryCount = 1
MatchExactHierarchy = False
SkipSetPropertyVerification = False
SendKeysAsScanCode = False
WaitForReadyLevel = UIThreadOnly
ContinueOnError = False
AlwaysSearchControls = False
 
Как я понял, функция должна ожидать WaitForReadyTimeout милисекунд и уже потом завершаться ошибкой. Я же планировал нажать на первую кнопку спустя несколько секунд после старта теста и увидеть, как тест нажмет на вторую кнопку, чтобы появилась третья :). Но все оказалось не так...
 
На самом деле: WaitForControlPropertyNotEqual ничего не ждет - алгоритм продолжается без всяких видимых! пауз и пытается нажать на кнопку, у которой нулевые высота и ширина, а BoundingRectangle вообще с отрицательными величинами (глянул на всякий случай). И сразу появляется ошибка:
...Microsoft.VisualStudio.TestTools.UITest.Extension.PlaybackFailureException: Cannot perform 'Click' on the control. Additional Details: The control details were not specified. ...
 
А если используем такое нажатие: Mouse.Click(second);, то ошибка примерно все равно о том же: ... 
Microsoft.VisualStudio.TestTools.UITest.Extension.FailedToPerformActionOnHiddenControlException: Cannot perform 'Click' on the hidden control. Additional Details: ...
 
Тогда возникает вопрос... а как тогда должны работать функции ожидания для UITestControl? Как их надо применять, чтобы отрабатывала задержка по времени и тест не валился, если вдруг что-то не успеет огтрисоваться или загрузиться в процессе запуска?
 
PS я пробовал и такой вариант: second.WaitForControlPropertyNotEqual(UITestControl.PropertyNames.Height, 0, 60000); - никакой разницы, все пролетает за считанные секунды и ошибки те же.