[英]Testing WPF ViewModel with Xunit
最近我被安排到一個新團隊工作,領導讓我為他們編寫的代碼編寫單元測試。 我真的不知道從哪里開始。 該應用程序是在 C# 中編寫的 WPF。我是 WPF 和 C# 的新手。他們提到使用 Xunit。
這是 class 的前兩個函數。我必須對受保護函數和公共函數進行單元測試,
class 類型是public class DataTableModel<T>: ViewModelBase
public DataTableModel()
{
//Setup Drag and Drop Commands:
RecordReceivedCommand = new DataTableReceivedCommand<T>(this);
RecordRemovedCommand = new DataTableRemovedCommand<T>(this);
RecordInsertedCommand = new DataTableInsertedCommand<T>(this);
View = CollectionViewSource.GetDefaultView(Records);
}
/// <summary>
/// This method sets up the properties for the table. By default, the first DisplayOption is selected, no search criteria is applies, and only 10 records should be returned.
/// </summary>
/// <param name="displayOption">The selected display option</param>
protected void InstantiateTable(int displayOption = 0)
{
_displayOption = displayOption;
_searchCriteria = "";
_displayAmount = "10";
_currentPage = 1;
_userMessage = "Retrieving data...";
TriggerUpdate();
}
這些函數是從上面調用的,所以我想我應該包括它們。
/// <summary>
/// This method is responsible for handling the updates to the recordset that the DataGrid will display.
/// Upon changes to the search criteria, display amount, display option and the current page, this method will be triggered
/// </summary>
/// <param name="resetPage">Inidicates whether the grid should return to page 1.</param>
protected void TriggerUpdate(bool resetPage = false)
{
_currentPage = resetPage ? 1 : _currentPage; //only reset to the first page if indicated
UserMessage = "Retrieving data...";
//Retrieve the records if the delegate has been set
GetRecords?.Invoke();
UserMessage = Records.Count == 0 ? "No data found" : "";
Message_ZIndex = !String.IsNullOrWhiteSpace(UserMessage) ? 1 : -1;
//update the page navigations button's settings when the pages are reset
UpdatePagination();
}
/// <summary>
/// Update the ability to navigate through the datatable's pages.
/// Upon changes the the search criteria, display amount, display option, the number of pages and the current page, this method will be
/// triggered
/// </summary>
private void UpdatePagination()
{
if (Pages > 1)
{
if (_currentPage == 1)
{
GoToFirstPage = false;
GoToPrevPage = false;
GoToNextPage = true;
GoToLastPage = true;
}
else if (_currentPage == Pages)
{
GoToFirstPage = true;
GoToPrevPage = true;
GoToNextPage = false;
GoToLastPage = false;
}
else
{
GoToFirstPage = true;
GoToPrevPage = true;
GoToNextPage = true;
GoToLastPage = true;
}
}
else
{
GoToFirstPage = false;
GoToPrevPage = false;
GoToNextPage = false;
GoToLastPage = false;
}
}
/// <summary>
/// Get's the information for the datatable's columns
/// (NOTE: This method is currently only called via ".Invoke" within the DataTable control.)
/// </summary>
/// <returns>A List of the DataTableDisplay attributes for the columns in the table</returns>
public List<DataTableDisplay> GetTableColumnInformation()
{
List<DataTableDisplay> attributes = new List<DataTableDisplay>();
PropertyInfo[] properties = typeof(T).GetProperties();
foreach(PropertyInfo property in properties)
{
DataTableDisplay attr = property.GetCustomAttribute<DataTableDisplay>(false);
if (attr != null && !String.IsNullOrWhiteSpace(attr.ColumnTitle))
{
attributes.Add(attr);
}
}
return attributes;
}
起初我想測試構造函數,但后來我不知道如何檢查命令變量,所以我轉到InstantiateTable
function。這就是我得到的。 仍然必須創建 object 所以使用構造函數但是當我嘗試調用x.InstantiateTable()
時我注意到它受到保護所以我不能這樣做。
用於創建 DataTableModel Object 的測試用例。它具有動態類型,所以我只是將其設為 int。
[Fact]
public void DataTableModel_Created()
{
DataTableModel<int> x = new DataTableModel<int>();
Assert.Equal(0, x.DisplayOption);
Assert.Equal("", x.SearchCriteria);
Assert.Equal("10", x.DisplayAmount);
Assert.Equal(1, x.CurrentPage);
Assert.Equal("Retrieving data...", x.UserMessage);
}
另一個受保護的 function 我必須進行單元測試。
protected List<T> UpdateTable(List<T> data)
{
_totalCount = data.Count;
int recordLimit = String.IsNullOrEmpty(DisplayAmount) ? 0 : Convert.ToInt32(DisplayAmount);
int startingRecord = recordLimit == 0 ? 0 : (_currentPage == 1 ? 1 : 1 + (recordLimit * (_currentPage - 1)));
int recordsToSkip = (CurrentPage - 1) * recordLimit;
int endingRecord = recordLimit == 0 ? 0 : recordLimit > _totalCount ? _totalCount : startingRecord + recordLimit - 1;
Pages = recordLimit == 0 ? 0 : _totalCount <= recordLimit ? 1 : _totalCount % recordLimit > 0 ? (_totalCount / recordLimit) + 1 : _totalCount / recordLimit;
List<T> displayRecords = data.Skip(recordsToSkip).Take(recordLimit).ToList();
DisplayMessage = endingRecord <= _totalCount ? $"Showing {(displayRecords.Count == 0 ? 0 : startingRecord)} to {endingRecord} of {_totalCount} entries" : $"Showing {startingRecord} to {_totalCount} of {_totalCount} entries";
return displayRecords;
}
還有一個代表 function? 我應該測試
public delegate List<T> Filter(List<T> data);
我不確定如何處理。 我在大學里做的幾個單元測試都是基本對象,沒有調用其他對象等等。 我已經閱讀了 mocking 和存根對象,我認為模擬數據庫來填充表可能會很好。
不要對函數進行單元測試。 單元測試視圖模型。
視圖模型將 XAML 綁定的屬性和命令公開給 GUI 控件,以便用戶可以與它們交互。
所有這些屬性和命令都已經是public
,否則它們不會從 XAML 可見,因此您的測試也可以訪問它們。
大多數 C# 程序員在將private
和protected
轉換為public
或轉換為internal
然后使用InternalsVisibleTo
之前不會三思而后行,但在我看來這都是錯誤的。 我們必須針對接口進行測試,而不是針對實現; 因此,我們必須進行黑盒測試,而不是白盒測試。
在 WPF 中測試視圖模型時,這意味着您應該只測試暴露給 XAML 的東西。
因此,您的測試應該將值設置為視圖模型屬性,就好像這些值是由 GUI 控件設置的一樣,調用視圖模型命令,就好像這些命令是由 GUI 按鈕觸發的一樣。
然后,您的測試應該只檢查視圖模型屬性的值如何因此發生變化。 這就是用戶會在屏幕上看到的內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.