AutomationID 속성 사용
참고 |
---|
이 문서는 System.Windows.Automation 네임스페이스에 정의된 관리되는 UI Automation 클래스를 사용하려는 .NET Framework 개발자를 위해 작성되었습니다.UI Automation에 대한 최신 정보는 Windows Automation API: UI Automation을 참조하십시오. |
이 항목에는 UI Automation 트리 내에서 요소를 배치하는 데 AutomationIdProperty를 사용하는 방법과 시기를 보여 주는 시나리오에 대한 설명과 샘플 코드가 나와 있습니다.
AutomationIdProperty는 UI 자동화 요소를 형제 요소와 구별하여 고유하게 식별합니다. 컨트롤 식별과 관련된 속성 식별자에 대한 자세한 내용은 UI 자동화 속성 개요를 참조하십시오.
참고 |
---|
AutomationIdProperty는 트리 전체에서 고유한 ID가 사용되도록 보장하지 않으며 일반적으로 유용하게 사용하려면 컨테이너와 범위 정보가 있어야 합니다.예를 들어 여러 개의 상위 수준 메뉴 항목과 그 아래에 다시 여러 개의 자식 메뉴 항목이 포함된 메뉴 컨트롤이 응용 프로그램에 있을 수 있습니다.이러한 하위 메뉴 항목은 "Item1", "Item 2" 등의 일반적인 체계로 식별되어 여러 상위 수준 메뉴 항목의 해당 자식 항목에 대해 ID가 중복 사용될 수 있습니다. |
시나리오
다음 세 가지 기본적인 UI 자동화 클라이언트 응용 프로그램 시나리오에서는 요소를 검색할 때 정확하고 일관된 결과를 얻기 위해 AutomationIdProperty를 사용해야 합니다.
참고 |
---|
AutomationIdProperty는 컨트롤 뷰에서 최상위 응용 프로그램 창, ID 또는 x:Uid가 없는 Windows Presentation Foundation (WPF) 컨트롤에서 파생된 UI 자동화 요소 및 컨트롤 ID가 없는 Win32 컨트롤에서 파생된 UI 자동화 요소를 제외한 모든 UI 자동화 요소에서 지원됩니다. |
고유하며 검색 가능한 AutomationID를 사용하여 UI 자동화 트리의 특정 요소 찾기
- UI Spy 같은 도구를 사용하여 관심 있는 UI 요소의 AutomationIdProperty를 보고합니다. 그러면 이 값을 복사하여 후속 자동화 테스트를 위한 테스트 스크립트와 같은 클라이언트 응용 프로그램에 붙여 넣을 수 있습니다. 이 방식을 사용하면 런타임에 요소를 식별하고 찾는 데 필요한 코드를 줄이고 단순화할 수 있습니다.
주의 |
---|
일반적으로 RootElement의 바로 아래 자식 요소만 가져와야 합니다.모든 하위 항목을 검색하면 수백 또는 수천 개의 요소에서 검색 작업이 반복되어 스택 오버플로가 발생할 수 있습니다.하위 수준에 있는 특정 요소를 찾으려는 경우 하위 수준에 있는 컨테이너나 응용 프로그램 창에서 검색을 시작해야 합니다. |
'''--------------------------------------------------------------------
''' <summary>
''' Finds all elements in the UI Automation tree that have a specified
''' AutomationID.
''' </summary>
''' <param name="targetApp">
''' The root element from which to start searching.
''' </param>
''' <param name="automationID">
''' The AutomationID value of interest.
''' </param>
''' <returns>
''' The collection of automation elements that have the specified
''' AutomationID value.
''' </returns>
'''--------------------------------------------------------------------
Private Function FindElementFromAutomationID( _
ByVal targetApp As AutomationElement, _
ByVal automationID As String) As AutomationElementCollection
Return targetApp.FindAll( _
TreeScope.Descendants, _
New PropertyCondition( _
AutomationElement.AutomationIdProperty, automationID))
End Function 'FindElementFromAutomationID
///--------------------------------------------------------------------
/// <summary>
/// Finds all elements in the UI Automation tree that have a specified
/// AutomationID.
/// </summary>
/// <param name="targetApp">
/// The root element from which to start searching.
/// </param>
/// <param name="automationID">
/// The AutomationID value of interest.
/// </param>
/// <returns>
/// The collection of UI Automation elements that have the specified
/// AutomationID value.
/// </returns>
///--------------------------------------------------------------------
private AutomationElementCollection FindElementFromAutomationID(AutomationElement targetApp,
string automationID)
{
return targetApp.FindAll(
TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, automationID));
}
앞서 식별한 AutomationElement를 반환하는 영구 경로 사용
- 클라이언트 응용 프로그램은 단순한 테스트 스크립트에서 강력한 기록 및 재생 유틸리티에 이르기까지 다양하며, 이러한 클라이언트 응용 프로그램은 파일 열기 대화 상자나 메뉴 항목과 같이 현재 인스턴스화되지 않아 UI 자동화 트리에 존재하지 않는 요소에 액세스해야 하는 경우가 있을 수 있습니다. 이러한 요소는 AutomationID 등의 UI Automation 속성을 사용하거나 컨트롤 패턴 및 이벤트 수신기를 통해 특정 user interface (UI) 작업 시퀀스를 재현하거나 "재생"하는 방법으로만 인스턴스화할 수 있습니다. Microsoft UI Automation를 사용하여 user interface (UI)와의 사용자 상호 작용에 기반하는 테스트 스크립트를 생성하는 예제를 보려면 Test Script Generator Sample을 참조하십시오.
'''--------------------------------------------------------------------
''' <summary>
''' Creates a UI Automation thread.
''' </summary>
''' <param name="sender">Object that raised the event.</param>
''' <param name="e">Event arguments.</param>
''' <remarks>
''' UI Automation must be called on a separate thread if the client
''' application itself could become a target for event handling.
''' For example, focus tracking is a desktop event that could involve
''' the client application.
''' </remarks>
'''--------------------------------------------------------------------
Private Sub CreateUIAThread(ByVal sender As Object, ByVal e As EventArgs)
' Start another thread to do the UI Automation work.
Dim threadDelegate As New ThreadStart(AddressOf CreateUIAWorker)
Dim workerThread As New Thread(threadDelegate)
workerThread.Start()
End Sub 'CreateUIAThread
'''--------------------------------------------------------------------
''' <summary>
''' Delegated method for ThreadStart. Creates a UI Automation worker
''' class that does all UI Automation related work.
''' </summary>
'''--------------------------------------------------------------------
Public Sub CreateUIAWorker()
uiautoWorker = New UIAWorker(targetApp)
End Sub 'CreateUIAWorker
Private uiautoWorker As UIAWorker
...
'''--------------------------------------------------------------------
''' <summary>
''' Function to playback through a series of recorded events calling
''' a WriteToScript function for each event of interest.
''' </summary>
''' <remarks>
''' A major drawback to using AutomationID for recording user
''' interactions in a volatile UI is the probability of catastrophic
''' change in the UI. For example, the 'Processes' dialog where items
''' in the listbox container can change with no input from the user.
''' This mandates thtat a record and playback application must be
''' reliant on the tester owning the UI being tested. In other words,
''' there has to be a contract between the provider and client that
''' excludes uncontrolled, external applications. The added benefit
''' is the guarantee that each control in the UI should have an
''' AutomationID assigned to it.
'''
''' This function relies on a UI Automation worker class to create
''' the System.Collections.Generic.Queue object that stores the
''' information for the recorded user interactions. This
''' allows post-processing of the recorded items prior to actually
''' writing them to a script. If this is not necessary the interaction
''' could be written to the script immediately.
''' </remarks>
'''--------------------------------------------------------------------
Private Sub Playback(ByVal targetApp As AutomationElement)
Dim element As AutomationElement
Dim storedItem As ElementStore
For Each storedItem In uiautoWorker.elementQueue
Dim propertyCondition As New PropertyCondition( _
AutomationElement.AutomationIdProperty, storedItem.AutomationID)
' Confirm the existence of a control.
' Depending on the controls and complexity of interaction
' this step may not be necessary or may require additional
' functionality. For example, to confirm the existence of a
' child menu item that had been invoked the parent menu item
' would have to be expanded.
element = targetApp.FindFirst( _
TreeScope.Descendants, propertyCondition)
If element Is Nothing Then
' Control not available, unable to continue.
' TODO: Handle error condition.
Return
End If
WriteToScript(storedItem.AutomationID, storedItem.EventID)
Next storedItem
End Sub 'Playback
'''--------------------------------------------------------------------
''' <summary>
''' Generates script code and outputs the code to a text control in
''' the client.
''' </summary>
''' <param name="automationID">
''' The AutomationID of the current control.
''' </param>
''' <param name="eventID">
''' The event recorded on that control.
''' </param>
'''--------------------------------------------------------------------
Private Sub WriteToScript( _
ByVal automationID As String, ByVal eventID As String)
' Script code would be generated and written to an output file
' as plain text at this point, but for the
' purposes of this example we just write to the console.
Console.WriteLine(automationID + " - " + eventID)
End Sub 'WriteToScript
///--------------------------------------------------------------------
/// <summary>
/// Creates a UI Automation thread.
/// </summary>
/// <param name="sender">Object that raised the event.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>
/// UI Automation must be called on a separate thread if the client
/// application itself could become a target for event handling.
/// For example, focus tracking is a desktop event that could involve
/// the client application.
/// </remarks>
///--------------------------------------------------------------------
private void CreateUIAThread(object sender, EventArgs e)
{
// Start another thread to do the UI Automation work.
ThreadStart threadDelegate = new ThreadStart(CreateUIAWorker);
Thread workerThread = new Thread(threadDelegate);
workerThread.Start();
}
///--------------------------------------------------------------------
/// <summary>
/// Delegated method for ThreadStart. Creates a UI Automation worker
/// class that does all UI Automation related work.
/// </summary>
///--------------------------------------------------------------------
public void CreateUIAWorker()
{
uiautoWorker = new FindByAutomationID(targetApp);
}
private FindByAutomationID uiautoWorker;
...
///--------------------------------------------------------------------
/// <summary>
/// Function to playback through a series of recorded events calling
/// a WriteToScript function for each event of interest.
/// </summary>
/// <remarks>
/// A major drawback to using AutomationID for recording user
/// interactions in a volatile UI is the probability of catastrophic
/// change in the UI. For example, the //Processes// dialog where items
/// in the listbox container can change with no input from the user.
/// This mandates thtat a record and playback application must be
/// reliant on the tester owning the UI being tested. In other words,
/// there has to be a contract between the provider and client that
/// excludes uncontrolled, external applications. The added benefit
/// is the guarantee that each control in the UI should have an
/// AutomationID assigned to it.
///
/// This function relies on a UI Automation worker class to create
/// the System.Collections.Generic.Queue object that stores the
/// information for the recorded user interactions. This
/// allows post-processing of the recorded items prior to actually
/// writing them to a script. If this is not necessary the interaction
/// could be written to the script immediately.
/// </remarks>
///--------------------------------------------------------------------
private void Playback(AutomationElement targetApp)
{
AutomationElement element;
foreach(ElementStore storedItem in uiautoWorker.elementQueue)
{
PropertyCondition propertyCondition =
new PropertyCondition(
AutomationElement.AutomationIdProperty, storedItem.AutomationID);
// Confirm the existence of a control.
// Depending on the controls and complexity of interaction
// this step may not be necessary or may require additional
// functionality. For example, to confirm the existence of a
// child menu item that had been invoked the parent menu item
// would have to be expanded.
element = targetApp.FindFirst(TreeScope.Descendants, propertyCondition);
if(element == null)
{
// Control not available, unable to continue.
// TODO: Handle error condition.
return;
}
WriteToScript(storedItem.AutomationID, storedItem.EventID);
}
}
///--------------------------------------------------------------------
/// <summary>
/// Generates script code and outputs the code to a text control in
/// the client.
/// </summary>
/// <param name="automationID">
/// The AutomationID of the current control.
/// </param>
/// <param name="eventID">
/// The event recorded on that control.
/// </param>
///--------------------------------------------------------------------
private void WriteToScript(string automationID, string eventID)
{
// Script code would be generated and written to an output file
// as plain text at this point, but for the
// purposes of this example we just write to the console.
Console.WriteLine(automationID + " - " + eventID);
}
앞서 식별한 AutomationElement를 반환하는 상대 경로 사용
- AutomationID는 형제 요소 간에서만 고유성이 보장되기 때문에 일부 경우 UI 자동화 트리의 여러 요소가 동일한 AutomationID 속성 값을 가질 수 있습니다. 이런 경우에는 부모(필요한 경우 부모의 상위 항목)를 기준으로 요소를 고유하게 식별할 수 있습니다. 예를 들어 개발자가 여러 자식 메뉴 항목을 포함하는 여러 메뉴 항목으로 구성된 메뉴 모음을 제공할 때 자식 메뉴 항목은 "Item1", "Item2" 등과 같은 순차적인 AutomationID로 식별됩니다. 이 경우 메뉴 항목의 부모(필요한 경우 부모의 상위 항목) AutomationID와 메뉴 항목의 AutomationID를 사용하여 각 메뉴 항목을 고유하게 식별할 수 있습니다.