Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Ahora que los viewModels y los servicios se encuentran en una biblioteca de clases independiente, puede crear fácilmente pruebas unitarias. Agregar proyectos de prueba unitaria le permite comprobar que los viewModels y los servicios se comportan según lo esperado sin depender de la capa de interfaz de usuario ni de las pruebas manuales. Puede ejecutar pruebas unitarias automáticamente como parte del flujo de trabajo de desarrollo, asegurándose de que el código sigue siendo confiable y fácil de mantener.
Creación de un proyecto de prueba unitaria
- Haga clic con el botón derecho en la solución en el Explorador de soluciones.
- Seleccione Agregar>nuevo proyecto....
- Elija la plantilla Aplicación de prueba unitaria de WinUI y seleccione Siguiente.
- Asigne al proyecto el nombre
WinUINotes.Testsy seleccione Crear.
Agregar referencias de proyecto
- Haga clic con el botón derecho en el proyecto WinUINotes.Tests y seleccione Agregar>referencia de proyecto....
- Compruebe el proyecto WinUINotes.Bus y seleccione Aceptar.
Creación de implementaciones falsas para pruebas
Para las pruebas, cree implementaciones falsas del servicio de archivos y las clases de almacenamiento que realmente no escriben en disco. Fakes son implementaciones ligeras que simulan el comportamiento de las dependencias reales con fines de prueba.
En el proyecto WinUINotes.Tests , cree una carpeta denominada Fakes.
Agregue un archivo
FakeFileService.csde clase en la carpeta 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; } } }FakeFileServiceutiliza un diccionario en memoria (fileStorage) para simular operaciones de archivo sin tocar el sistema de archivos real. Entre las características clave se incluyen:-
Simulación asincrónica: se usa
Task.Delay(10)para imitar las operaciones reales de archivos asincrónicos. - Validación: produce excepciones para entradas no válidas, al igual que la implementación real.
-
Integración con clases de almacenamiento falsas: devuelve
FakeStorageFoldereFakeStorageFileinstancias que funcionan conjuntamente para simular la API de Almacenamiento de Windows
-
Simulación asincrónica: se usa
Agregue
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 ... } }FakeStorageFoldertoma el diccionario de almacenamiento de archivos en su constructor, lo que le permite trabajar con el mismo sistema de archivos en memoria queFakeFileService. La mayoría de los miembros de interfaz lanzanNotImplementedException, ya que solo es necesario implementar las propiedades y los métodos realmente utilizados por las pruebas.Puede ver la implementación completa de
FakeStorageFolderen el repositorio de código de GitHub para este tutorial.Agregue
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 ... } }FakeStorageFilerepresenta archivos individuales en el sistema de almacenamiento falso. Almacena el nombre de archivo y proporciona la implementación mínima necesaria para las pruebas. Al igual queFakeStorageFolder, solo implementa los miembros que realmente son utilizados por el código que se está probando.Puede ver la implementación completa de
FakeStorageFolderen el repositorio de código de GitHub para este tutorial.
Obtenga más información en los documentos:
- Procedimientos recomendados para las pruebas unitarias
- Dobles de prueba (falsificaciones, simulacros, soportes)
Escritura de una prueba unitaria sencilla
Cambie el nombre
UnitTest1.csaNoteTests.csy actualícelo: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); } } }En esta prueba se muestra cómo realizar una prueba unitaria del
NoteViewModelusando elFakeFileService. La prueba crea un nuevoNoteViewModel, comprueba su estado inicial (la fecha es reciente, el nombre de archivo sigue el patrón esperado), establece el texto en la nota, ejecuta el comando save y confirma que el texto persiste. Dado que el servicio de archivos falso se usa en lugar de la implementación real, la prueba se ejecuta rápidamente sin ninguna E/S de archivo real y se puede ejecutar repetidamente sin efectos secundarios.
Obtenga más información en los documentos:
Ejecución de las pruebas
- Abra la ventana Explorador de pruebas en Visual Studio (Explorador de pruebas).>
- Seleccione Ejecutar todas las pruebas para ejecutar la prueba unitaria.
- Compruebe que se supera la prueba.
Ahora tienes una arquitectura testeable donde puedes probar los ViewModels y los servicios independientemente de la interfaz de usuario.
Resumen
En esta serie de tutoriales, ha aprendido a:
- Cree un proyecto de biblioteca de clases independiente (proyecto bus) para contener los viewModels y los servicios, lo que permite realizar pruebas unitarias independientes de la capa de interfaz de usuario.
- Implemente el patrón MVVM mediante el MVVM Toolkit, aprovechando los atributos
ObservableObject,[ObservableProperty]y[RelayCommand]para reducir el código repetitivo. - Use generadores de origen para crear automáticamente las notificaciones de cambio de propiedad y las implementaciones de comandos.
- Use
[NotifyCanExecuteChangedFor]para actualizar automáticamente la disponibilidad de comandos cuando cambien los valores de propiedad. - Integre la inserción de dependencias mediante
Microsoft.Extensions.DependencyInjectionpara administrar el ciclo de vida de ViewModels y los servicios. - Cree una
IFileServiceinterfaz e implementación para controlar las operaciones de archivo de una manera verificable. - Configure el contenedor DI en
App.xaml.csy recupere ViewModels del proveedor de servicios en tus páginas. - Implemente el
WeakReferenceMessengerpara habilitar un acoplamiento flexible entre componentes, permitiendo que las páginas respondan a eventos de ViewModel sin referencias directas. - Cree clases de mensaje que hereden de
ValueChangedMessage<T>para transportar datos entre componentes. - Cree implementaciones falsas de dependencias para realizar pruebas sin tocar el sistema de archivos real.
- Escriba pruebas unitarias mediante MSTest para comprobar el comportamiento de ViewModel independientemente de la capa de interfaz de usuario.
Esta arquitectura proporciona una base sólida para crear aplicaciones WinUI que se pueden mantener y probar con una separación clara de los problemas entre la interfaz de usuario, la lógica de negocios y las capas de acceso a datos. Puede descargar o ver el código de este tutorial desde el repositorio de GitHub.
Pasos siguientes
Ahora que comprende cómo implementar MVVM con el kit de herramientas de MVVM y la inserción de dependencias, puede explorar temas más avanzados:
- Mensajería avanzada: explore patrones de mensajería adicionales, incluidos los mensajes de solicitud/respuesta y los tokens de mensaje para el control selectivo de mensajes.
- Validación: agregue la validación de entrada a viewModels mediante anotaciones de datos y las características de validación del kit de herramientas de MVVM.
-
Comandos asincrónicos: obtenga más información sobre la ejecución asincrónica de comandos, la compatibilidad con la cancelación y los informes de progreso con
AsyncRelayCommand. - Pruebas avanzadas: explore escenarios de prueba más avanzados, como probar el control de mensajes, la ejecución de comandos asincrónicos y las notificaciones de cambio de propiedades.
-
Colecciones observables: use
ObservableCollection<T>eficazmente y exploreObservableRangeCollection<T>para las operaciones masivas.