DevExpress WinForms中文教程 - 如何通過UI測試自動化增強應用可靠性?(二)

DevExpress WinForm擁有180+組件和UI庫,能爲Windows Forms平臺創建具有影響力的業務解決方案。DevExpress WinForm能完美構建流暢、美觀且易於使用的應用程序,無論是Office風格的界面,還是分析處理大批量的業務數據,它都能輕鬆勝任!

UI自動化測試利用特定的工具/框架來模擬用戶與界面的交互,並幫助確保應用程序滿足相關的最終用戶需求。當與其他測試方法(API測試、單元測試等)結合使用時,UI自動化可以提高應用程序的穩定性,減少花在手工測試上的時間,當然還可以提高用戶滿意度。在本文中,我們將向您展示如何使用UI自動化在Visual Studio 2022中編寫簡單/高級UI測試。

DevExpress WinForms中文教程圖集

 

在開始之前,我們先看看UI測試的優勢:

  • UI測試以應用程序爲目標,允許您測試應用程序流(端到端測試),涵蓋應用程序的所有元素,包括UI和業務邏輯(而單元測試側重於測試應用程序中的單個模塊、類或組件)。
  • UI測試有助於識別與導航、數據輸入和跨不同屏幕的工作流相關的問題,這些問題可能不會被其他測試捕獲。
  • UI測試爲測試複雜場景和邊緣情況提供了效率和可伸縮性(單元測試對於測試單個代碼單元是必不可少的)。請注意,UI測試可能需要更長的時間來執行,因爲它們與UI交互,並在應用程序開發管道中稍後運行(單元測試通常更快,並且在提交到存儲庫之前進行了檢查)。

獲取DevExpress v23.2.5正式版下載

在上文中(點擊這裏回顧>>),我們爲大家介紹了UI測試自動化是如何工作的、開始創建UI自動化測試等,本文將繼續介紹如何創還能UI自動化測試。

創建UI自動化測試

3. 爲登錄表單創建測試

在進行測試之前,我想澄清幾點:

  • AutomationElement.RootElement靜態屬性包含根元素,使用此屬性訪問應用程序。
  • AutomationElement.FindFirst方法允許您找到一個特定的UI元素:

AutomationElement logInFormElement = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "CRM Log In Form"));

FindFirst方法有兩個參數,第一個參數(scope)指定搜索的範圍,第二個參數(條件)指定要匹配的標準(在我的例子中,這是一個AutomationName = "CRM Log In Form"的表單)。

UI控件可以根據其他設置(例如,Text)自動“計算”AutomationName。
如果需要,您可以顯式地設置AutomationName屬性或處理DXAccessible.QueryAccessibleInfo事件,來向DevExpress UI元素提供可訪問性信息。

  • 在某些情況下,被測試的應用程序可能沒有時間生成UI元素,因爲UI自動化框架執行操作非常快。因此,我們建議使用“poll”間隔。
    下面的例子實現了FindFirstWithTimeout方法,並反覆調用FindFirst(帶有延遲),直到找到指定的UI元素:
public static class AutomationElementExtensions {
public static AutomationElement FindFirstWithTimeout(this AutomationElement @this,
TreeScope scope, Condition condition, int timeoutMilliseconds = 1000) {
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
do {
var result = @this.FindFirst(scope, condition);
if (result != null)
return result;
Thread.Sleep(100);
}
while (stopwatch.ElapsedMilliseconds < timeoutMilliseconds);
return null;
}
}

下面的測試將執行以下操作:

  • 輸入錯誤的登錄名和密碼。
  • 確保在“LogIn”表單中顯示錯誤消息。
[Test]
public void NonExistingUsernameLoginTest() {
afterLogInAction = CheckErrorLabel;
LogIn("TestNonExistingUser", "123456");
}
void CheckErrorLabel() {
AutomationElement errorLabelElement = loginForm.FindFirstByNameWithTimeout(
TreeScope.Children,
"Invalid User or Password",
10000);
Assert.IsNotNull(errorLabelElement);
}
void LogIn(string username, string password) {
// Finds the LogIn form and its main UI elements.
loginForm = AutomationElement.RootElement.FindFirstByNameWithTimeout(
TreeScope.Children,
logInFormAccessbleName,
10000);
AutomationElement usernameElement = loginForm.FindFirstByNameWithTimeout(TreeScope.Children, usernameAccessbleName, 10000);
AutomationElement passwordElement = loginForm.FindFirstByNameWithTimeout(TreeScope.Children, passwordAccessbleName, 10000);
AutomationElement logInButtonElement = loginForm.FindFirstByNameWithTimeout(TreeScope.Children, logInButtonAccessbleName, 10000);

// Gets automation patterns to fill "UserName" and "Password" inputs (editors).
ValuePattern usernameValuePattern = (ValuePattern)usernameElement.GetCurrentPattern(ValuePattern.Pattern);
ValuePattern passwordValuePattern = (ValuePattern)passwordElement.GetCurrentPattern(ValuePattern.Pattern);
InvokePattern invokePattern = (InvokePattern)logInButtonElement.GetCurrentPattern(InvokePattern.Pattern);

// Sets editor values. Fills in username and password input fields.
usernameValuePattern.SetValue(username);
passwordValuePattern.SetValue(password);
invokePattern.Invoke();

// Performs an action after a log in attempt.
afterLogInAction?.Invoke();
}

正如您所看到的,編寫測試可以歸結爲獲取一個AutomationElement並調用它的模式方法。

4. 爲客戶表單創建一個測試

讓我們考慮一個更復雜的情況,並在DevExpress WinForm數據網格(GridControl)中測試數據編輯。DevExpress數據網格包含一個帶有布爾值的“Is Modified”未綁定列,該列的值表示用戶是否修改了“Name”列的值。

我使用TablePattern與網格控件一起工作,下面的例子展示瞭如何編寫一個測試來修改我們的WinForms Grid中的客戶名稱,並檢查“Is Modified”列中的值是否從false變爲true:

[Test]
public void ModifiedCustomerTest() {
LogIn(testExistingUserLogin, testExistingUserPassword);

// Finds the GridControl and gets its TablePattern.
customersForm = AutomationElement.RootElement.FindFirstByNameWithTimeout(
TreeScope.Children,
customersFormAccessbleName,
10000);
AutomationElement customersGrid = customersForm.FindFirstByIdWithTimeout(
TreeScope.Children,
customersGridAutomationID,
10000);
TablePattern customersTablePattern = (TablePattern)customersGrid.GetCurrentPattern(TablePattern.Pattern);

// Activates a cell within the GridControl.
AutomationElement cellToUpdate = customersTablePattern.GetItem(1, 1);
InvokePattern testCellInvokePattern = (InvokePattern)cellToUpdate.GetCurrentPattern(InvokePattern.Pattern);
testCellInvokePattern.Invoke();

// Modifies the cell's value.
AutomationElement editingControl = customersGrid.FindFirstByNameWithTimeout(TreeScope.Descendants, "Editing control", 1000);
ValuePattern editedCellValuePattern = (ValuePattern)editingControl.GetCurrentPattern(ValuePattern.Pattern);
editedCellValuePattern.SetValue("Value updated!");
Thread.Sleep(1000); // Sets a delay for demonstration purposes.

// Selects the next data row.
AutomationElement nextRowCell = customersTablePattern.GetItem(2, 1);
SelectionItemPattern selectionItemPattern = (SelectionItemPattern)TreeWalker.ControlViewWalker.GetParent(nextRowCell).GetCurrentPattern(SelectionItemPattern.Pattern);
selectionItemPattern.Select();
Thread.Sleep(1000);

// Checks if the value in the "Is Modified" column has changed.
int isModiedColumnIndex = customersTablePattern.Current.GetColumnHeaders().ToList().FindIndex(h => h.Current.Name == "Is Modified");
AutomationElement isModifiedCell = customersTablePattern.GetItem(1, isModiedColumnIndex);
ValuePattern isModifiedCellValuePattern = (ValuePattern)isModifiedCell.GetCurrentPattern(ValuePattern.Pattern);
Assert.AreEqual(isModifiedCellValuePattern.Current.Value, "Checked");
}

5. 運行測試

要運行我剛剛創建的測試,將用tests展開項目(“TestRunner”),右鍵單擊*.cs文件來調用上下文菜單,然後單擊"Run Tests"。

DevExpress WinForms中文教程圖集
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章