Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Теперь, когда viewModels и службы находятся в отдельной библиотеке классов, можно легко создавать модульные тесты. Добавление проектов модульного теста позволяет убедиться, что viewModels и службы работают должным образом, не опираясь на уровень пользовательского интерфейса или ручное тестирование. Модульные тесты можно запускать автоматически в рамках рабочего процесса разработки, обеспечивая надежность и обслуживание кода.
Создание проекта модульного теста
- Щелкните правой кнопкой мыши решение в обозревателе решений.
- Выберите "Добавить>новый проект...".
- Выберите шаблон приложения модульного теста WinUI и нажмите кнопку "Далее".
- Назовите проект
WinUINotes.Testsи выберите Создать.
Добавление ссылок на проект
- Щелкните правой кнопкой мыши проект WinUINotes.Tests и выберите "Добавить>ссылку на проект...".
- Проверьте проект WinUINotes.Bus и нажмите кнопку "ОК".
Создание поддельных реализаций для тестирования
Для тестирования создайте поддельные реализации файловой службы и классов хранилища, которые фактически не записываются на диск. Упрощенные реализации, называемые фейками, имитируют поведение реальных зависимостей для целей тестирования.
В проекте WinUINotes.Tests создайте новую папку с именем Fakes.
Добавьте файл
FakeFileService.csкласса в папку Fakes:using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Windows.Storage; using WinUINotes.Services; namespace WinUINotes.Tests.Fakes { internal class FakeFileService : IFileService { private Dictionary<string, string> fileStorage = []; public async Task CreateOrUpdateFileAsync(string filename, string contents) { if (fileStorage.ContainsKey(filename)) { fileStorage[filename] = contents; } else { fileStorage.Add(filename, contents); } await Task.Delay(10); // Simulate some async work } public async Task DeleteFileAsync(string filename) { if (fileStorage.ContainsKey(filename)) { fileStorage.Remove(filename); } await Task.Delay(10); // Simulate some async work } public bool FileExists(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentException("Filename cannot be null or empty", nameof(filename)); } if (fileStorage.ContainsKey(filename)) { return true; } return false; } public IStorageFolder GetLocalFolder() { return new FakeStorageFolder(fileStorage); } public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync() { await Task.Delay(10); return GetStorageItemsInternal(); } public async Task<IReadOnlyList<IStorageItem>> GetStorageItemsAsync(IStorageFolder storageFolder) { await Task.Delay(10); return GetStorageItemsInternal(); } private IReadOnlyList<IStorageItem> GetStorageItemsInternal() { return fileStorage.Keys.Select(filename => CreateFakeStorageItem(filename)).ToList(); } private IStorageItem CreateFakeStorageItem(string filename) { return new FakeStorageFile(filename); } public async Task<string> GetTextFromFileAsync(IStorageFile file) { await Task.Delay(10); if (fileStorage.ContainsKey(file.Name)) { return fileStorage[file.Name]; } return string.Empty; } } }Этот компонент использует словарь в памяти (
fileStorage) для имитации операций с файлами, не взаимодействуя с фактической файловой системой. К ключевым функциям относятся:-
Асинхронное моделирование: используется
Task.Delay(10)для имитации реальных асинхронных операций файлов - Проверка. Создает исключения для недопустимых входных данных, как и реальная реализация
-
Интеграция с поддельными классами хранилища: возвращает
FakeStorageFolderиFakeStorageFileэкземпляры, которые работают вместе для имитации API хранилища Windows
-
Асинхронное моделирование: используется
Добавить
FakeStorageFolder.cs:using System; using System.Collections.Generic; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; namespace WinUINotes.Tests.Fakes { internal class FakeStorageFolder : IStorageFolder { private string name; private Dictionary<string, string> fileStorage = []; public FakeStorageFolder(Dictionary<string, string> files) { fileStorage = files; } public FileAttributes Attributes => throw new NotImplementedException(); public DateTimeOffset DateCreated => throw new NotImplementedException(); public string Name => name; public string Path => throw new NotImplementedException(); public IAsyncOperation<StorageFile> CreateFileAsync(string desiredName) { throw new NotImplementedException(); } public IAsyncOperation<StorageFile> CreateFileAsync(string desiredName, CreationCollisionOption options) { throw new NotImplementedException(); } public IAsyncOperation<StorageFolder> CreateFolderAsync(string desiredName) { throw new NotImplementedException(); } // Only partial implementation shown for brevity ... } }Конструктор принимает словарь хранилища файлов
FakeStorageFolder, что позволяет работать с той же файловой системой в памяти, что иFakeFileService. Большинство членов интерфейса выдает исключениеNotImplementedException, так как необходимо реализовать только свойства и методы, которые фактически используются тестами.Вы можете просмотреть полную реализацию
FakeStorageFolderв репозитории кода GitHub для этого руководства.Добавить
FakeStorageFile.cs:using System; using System.IO; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Streams; namespace WinUINotes.Tests.Fakes { public class FakeStorageFile : IStorageFile { private string name; public FakeStorageFile(string name) { this.name = name; } public string ContentType => throw new NotImplementedException(); public string FileType => throw new NotImplementedException(); public FileAttributes Attributes => throw new NotImplementedException(); public DateTimeOffset DateCreated => throw new NotImplementedException(); public string Name => name; public string Path => throw new NotImplementedException(); public IAsyncOperation<StorageFile> CopyAsync(IStorageFolder destinationFolder) { throw new NotImplementedException(); } public IAsyncOperation<StorageFile> CopyAsync(IStorageFolder destinationFolder, string desiredNewName) { throw new NotImplementedException(); } public IAsyncOperation<StorageFile> CopyAsync(IStorageFolder destinationFolder, string desiredNewName, NameCollisionOption option) { throw new NotImplementedException(); } public IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) { throw new NotImplementedException(); } // Only partial implementation shown for brevity ... } }Переменная
FakeStorageFileобозначает отдельные файлы в фальшивой системе хранения. Он сохраняет имя файла и обеспечивает минимальную реализацию, необходимую для тестов. НапримерFakeStorageFolder, он реализует только элементы, которые фактически используются тестируемым кодом.Вы можете просмотреть полную реализацию
FakeStorageFolderв репозитории кода GitHub для этого руководства.
Дополнительные сведения см. в документации:
- Рекомендации по модульному тестированию
- Объекты-двойники для тестирования (фальшивые, моки, заглушки)
Создание простого модульного теста
Переименуйте
UnitTest1.csвNoteTests.csи обновите его.using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using WinUINotes.Tests.Fakes; namespace WinUINotes.Tests { [TestClass] public partial class NoteTests { [TestMethod] public void TestCreateUnsavedNote() { var noteVm = new ViewModels.NoteViewModel(new FakeFileService()); Assert.IsNotNull(noteVm); Assert.IsTrue(noteVm.Date > DateTime.Now.AddHours(-1)); Assert.IsTrue(noteVm.Filename.EndsWith(".txt")); Assert.IsTrue(noteVm.Filename.StartsWith("notes")); noteVm.Text = "Sample Note"; Assert.AreEqual("Sample Note", noteVm.Text); noteVm.SaveCommand.Execute(null); Assert.AreEqual("Sample Note", noteVm.Text); } } }В этом тесте показано, как проводить модульное тестирование
NoteViewModel, используяFakeFileService. Тест создает новоеNoteViewModel, проверяет его начальное состояние (дата недавняя, имя файла соответствует ожидаемому шаблону), записывает текст в заметку, выполняет команду сохранения и подтверждает, что текст сохраняется. Так как служба поддельных файлов используется вместо реальной реализации, тест выполняется быстро без фактического ввода-вывода файлов и может выполняться многократно без побочных эффектов.
Дополнительные сведения см. в документации:
- Обзор MSTest
- Класс Assert
Выполнение тестов
- Откройте окно обозревателя тестов в Visual Studio (обозреватель тестов>).
- Выберите "Выполнить все тесты ", чтобы выполнить модульный тест.
- Убедитесь, что тест проходит.
Теперь у вас есть тестируемая архитектура, в которой можно протестировать ViewModels и службы независимо от пользовательского интерфейса.
Сводка
В этой серии учебников вы узнали, как:
- Создайте отдельный проект библиотеки классов (Bus project) для хранения ваших ViewModels и служб, что позволяет проводить модульное тестирование отдельно от слоя пользовательского интерфейса.
- Реализуйте шаблон MVVM с помощью набора инструментов MVVM, используя атрибуты
ObservableObject,[ObservableProperty], и[RelayCommand]для уменьшения стандартного кода. - Используйте генераторы источников для автоматического создания уведомлений об изменении свойств и реализаций команд.
- Используется
[NotifyCanExecuteChangedFor]для автоматического обновления доступности команд при изменении значений свойств. - Интегрируйте внедрение зависимостей с помощью
Microsoft.Extensions.DependencyInjectionдля управления жизненным циклом ViewModels и служб. -
IFileServiceСоздайте интерфейс и реализацию для обработки операций с файлами с помощью тестового метода. - Настройте контейнер DI в
App.xaml.csи получите ViewModels от поставщика услуг на ваших страницах. -
WeakReferenceMessengerРеализуйте возможность свободного взаимодействия между компонентами, позволяя страницам реагировать на события ViewModel без прямых ссылок. - Создайте классы сообщений, наследуемые от
ValueChangedMessage<T>, для передачи данных между компонентами. - Создайте поддельные реализации зависимостей для тестирования без касания фактической файловой системы.
- Написание модульных тестов с помощью MSTest для проверки поведения ViewModel независимо от слоя пользовательского интерфейса.
Эта архитектура обеспечивает надежную основу для создания обслуживаемых, тестируемых приложений WinUI с четким разделением проблем между пользовательским интерфейсом, бизнес-логикой и уровнями доступа к данным. Вы можете скачать или просмотреть код для этого руководства из репозитория GitHub.
Дальнейшие шаги
Теперь, когда вы узнаете, как реализовать MVVM с помощью набора средств MVVM и внедрения зависимостей, вы можете изучить более сложные разделы:
- Расширенный обмен сообщениями. Изучение дополнительных шаблонов обмена сообщениями, включая сообщения запроса и ответа и маркеры сообщений для выборочной обработки сообщений.
- Проверка: Добавьте проверку данных в ViewModels с помощью аннотаций данных и функций проверки в инструментарии MVVM.
-
Асинхронные команды: узнайте больше об асинхронном выполнении команд, поддержке отмены и отчетах о ходе выполнения.
AsyncRelayCommand - Расширенное тестирование. Изучите более сложные сценарии тестирования, включая обработку сообщений, асинхронное выполнение команд и уведомления об изменении свойств.
-
Наблюдаемые коллекции: эффективное использование
ObservableCollection<T>и исследуйтеObservableRangeCollection<T>для массовых операций.
Связанный контент
Windows developer