Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Теперь, когда 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