簡體   English   中英

使用 Xunit 測試 WPF ViewModel

[英]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# 程序員在將privateprotected轉換為public或轉換為internal然后使用InternalsVisibleTo之前不會三思而后行,但在我看來這都是錯誤的。 我們必須針對接口進行測試,而不是針對實現; 因此,我們必須進行黑盒測試,而不是白盒測試。

在 WPF 中測試視圖模型時,這意味着您應該只測試暴露給 XAML 的東西。

因此,您的測試應該將值設置為視圖模型屬性,就好像這些值是由 GUI 控件設置的一樣,調用視圖模型命令,就好像這些命令是由 GUI 按鈕觸發的一樣。

然后,您的測試應該只檢查視圖模型屬性的值如何因此發生變化。 這就是用戶會在屏幕上看到的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM