Ejercicio: adición de pruebas unitarias a la aplicación
En esta unidad, agregaremos pruebas unitarias a la compilación automatizada que creamos con Microsoft Azure Pipelines. Los errores de regresión están pasando al código del equipo y están interrumpiendo la funcionalidad de filtrado de la tabla de clasificación. Más concretamente, sigue apareciendo el modo de juego incorrecto.
En la imagen siguiente se muestra el problema. Cuando un usuario selecciona "Vía láctea" para mostrar solo las puntuaciones de ese mapa de juego, obtiene resultados de otros mapas de juego, como Andrómeda.
El equipo quiere detectar el error antes de que llegue a los evaluadores. Las pruebas unitarias son una excelente manera de probar automáticamente los errores de regresión.
Agregar las pruebas unitarias en este momento del proceso dará al equipo una ventaja inicial a medida que se mejora la aplicación web de Space Game. La aplicación usa una base de datos de documentos para almacenar las puntuaciones más altas y los perfiles de los jugadores. En este momento, usa datos de prueba locales, pero planean conectar más adelante la aplicación a una base de datos activa.
Hay muchos marcos de trabajo de pruebas unitarias disponibles para aplicaciones de C#. Se usa NUnit porque es popular con la comunidad.
Esta es la prueba unitaria con la que trabaja:
[TestCase("Milky Way")]
[TestCase("Andromeda")]
[TestCase("Pinwheel")]
[TestCase("NGC 1300")]
[TestCase("Messier 82")]
public void FetchOnlyRequestedGameRegion(string gameRegion)
{
const int PAGE = 0; // take the first page of results
const int MAX_RESULTS = 10; // sample up to 10 results
// Form the query predicate.
// This expression selects all scores for the provided game region.
Expression<Func<Score, bool>> queryPredicate = score => (score.GameRegion == gameRegion);
// Fetch the scores.
Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
queryPredicate, // the predicate defined above
score => 1, // we don't care about the order
PAGE,
MAX_RESULTS
);
IEnumerable<Score> scores = scoresTask.Result;
// Verify that each score's game region matches the provided game region.
Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));
}
Puede filtrar la tabla de clasificación por cualquier combinación de tipo y mapa de juego.
Esta prueba busca en la tabla de clasificación las puntuaciones más altas y comprueba que cada resultado coincida con el mapa de juego proporcionado.
En un método de prueba NUnit, TestCase
proporciona datos insertados que se usan para probar ese método. En este caso, NUnit llama al método de prueba unitaria FetchOnlyRequestedGameRegion
de la siguiente manera:
FetchOnlyRequestedGameRegion("Milky Way");
FetchOnlyRequestedGameRegion("Andromeda");
FetchOnlyRequestedGameRegion("Pinwheel");
FetchOnlyRequestedGameRegion("NGC 1300");
FetchOnlyRequestedGameRegion("Messier 82");
Observe la llamada al método Assert.That
al final de la prueba. Una aserción es una condición o instrucción que se declara como verdadera. Si la condición resulta ser false, esto podría indicar un error en el código. NUnit ejecuta cada método de prueba con los datos insertados que ha especificado y registra el resultado como una prueba superada o con errores.
Muchos marcos de pruebas unitarias proporcionan métodos de verificación que se asemejan al lenguaje natural. Estos métodos ayudan a facilitar la lectura de las pruebas y su asignación a los requisitos de la aplicación.
Observe la aserción de este ejemplo:
Assert.That(scores, Is.All.Matches<Score>(score => score.GameRegion == gameRegion));
Podría leer esta línea de la siguiente manera:
Confirme que la región de juego de cada puntuación devuelta coincide con la región de juego proporcionada.
Este es el proceso que seguirá:
- Capture una rama del repositorio de GitHub que contenga las pruebas unitarias.
- Ejecute las pruebas localmente para comprobar que se superan.
- Agregue tareas a la configuración de canalización para ejecutar las pruebas y recopilar los resultados.
- Inserte la rama en el repositorio de GitHub.
- Vea cómo el proyecto de Azure Pipelines compila automáticamente la aplicación y ejecuta las pruebas.
Captura de la rama de GitHub
Aquí se capturará la rama unit-tests
de GitHub y se modificará o cambiará a esa rama.
Esta rama contiene el proyecto Space Game con el que ha trabajado en los módulos anteriores y una configuración de Azure Pipelines con la que empezar.
En Visual Studio Code, abra el terminal integrado.
Ejecute los comandos
git
siguientes para capturar una rama denominadaunit-tests
desde el repositorio de Microsoft y, después, cambie a esa rama.git fetch upstream unit-tests git checkout -B unit-tests upstream/unit-tests
El formato de este comando le permite obtener el código de inicio del repositorio de GitHub de Microsoft, conocido como
upstream
. En resumen, insertará esta rama en el repositorio de GitHub, conocido comoorigin
.Como paso opcional, abra el archivo azure-pipelines.yml desde Visual Studio Code y familiarícese con la configuración inicial. La configuración es similar a la básica creada en el módulo Crear una canalización de compilación con Azure Pipelines. Solo compila la configuración de versión de la aplicación.
Ejecución de las pruebas localmente
Se recomienda ejecutar todas las pruebas localmente antes de enviarlas a la canalización. Es lo que hará aquí.
En Visual Studio Code, abra el terminal integrado.
Ejecute
dotnet build
para compilar cada proyecto en la solución.dotnet build --configuration Release
Ejecute el siguiente comando
dotnet test
para realizar las pruebas unitarias:dotnet test --configuration Release --no-build
La marca
--no-build
especifica que no se compile el proyecto antes de ejecutarlo. No hace falta que compile el proyecto porque ya lo hizo en el paso anterior.Debería ver que se superan las cinco pruebas.
Starting test execution, please wait... A total of 1 test files matched the specified pattern. Passed! - Failed: 0, Passed: 5, Skipped: 0, Total: 5, Duration: 57 ms
En este ejemplo, las pruebas tardaron menos de un segundo en ejecutarse.
Observe que se realizaron cinco pruebas en total. Aunque solo se definió un método de prueba (
FetchOnlyRequestedGameRegion
), esta prueba se ejecuta cinco veces, una vez para cada mapa de juego, según lo especificado en los datos insertadosTestCase
.Ejecute las pruebas una segunda vez. Esta vez, proporcione la opción
--logger
para escribir los resultados en un archivo de registro.dotnet test Tailspin.SpaceGame.Web.Tests --configuration Release --no-build --logger trx
Verá en la salida que se habrá creado un archivo TRX en el directorio TestResults.
Un archivo TRX es un documento XML que contiene los resultados de una serie de pruebas. Es un formato popular para los resultados de las pruebas porque Visual Studio y otras herramientas pueden ayudarle a visualizar los resultados.
Más adelante, verá cómo Azure Pipelines puede ayudarle a visualizar y seguir los resultados de las pruebas mientras se ejecutan en la canalización.
Nota:
Los archivos TRX no están diseñados para incluirse en el control de código fuente. Un archivo .gitignore permite especificar los archivos temporales y de otro tipo que quiere que Git ignore. El archivo .gitignore del proyecto ya está configurado para ignorar todo el contenido del directorio TestResults.
Como paso opcional, abra el archivo DocumentDBRepository_GetItemsAsyncShould.cs desde la carpeta Tailspin.SpaceGame.Web.Tests en Visual Studio Code y examine el código de prueba. Incluso aunque no le interese compilar aplicaciones de .NET de forma específica, el código de prueba le resultará útil porque se parece al código que se ve en otros marcos de trabajo de pruebas unitarias.
Adición de tareas a la configuración de canalización
Aquí configurará la canalización de compilación para ejecutar las pruebas unitarias y recopilar los resultados.
En Visual Studio Code, modifique azure-pipelines.yml de la manera siguiente:
trigger: - '*' pool: vmImage: 'ubuntu-20.04' demands: - npm variables: buildConfiguration: 'Release' wwwrootDir: 'Tailspin.SpaceGame.Web/wwwroot' dotnetSdkVersion: '6.x' steps: - task: UseDotNet@2 displayName: 'Use .NET SDK $(dotnetSdkVersion)' inputs: version: '$(dotnetSdkVersion)' - task: Npm@1 displayName: 'Run npm install' inputs: verbose: false - script: './node_modules/.bin/node-sass $(wwwrootDir) --output $(wwwrootDir)' displayName: 'Compile Sass assets' - task: gulp@1 displayName: 'Run gulp tasks' - script: 'echo "$(Build.DefinitionName), $(Build.BuildId), $(Build.BuildNumber)" > buildinfo.txt' displayName: 'Write build info' workingDirectory: $(wwwrootDir) - task: DotNetCoreCLI@2 displayName: 'Restore project dependencies' inputs: command: 'restore' projects: '**/*.csproj' - task: DotNetCoreCLI@2 displayName: 'Build the project - $(buildConfiguration)' inputs: command: 'build' arguments: '--no-restore --configuration $(buildConfiguration)' projects: '**/*.csproj' - task: DotNetCoreCLI@2 displayName: 'Run unit tests - $(buildConfiguration)' inputs: command: 'test' arguments: '--no-build --configuration $(buildConfiguration)' publishTestResults: true projects: '**/*.Tests.csproj' - task: DotNetCoreCLI@2 displayName: 'Publish the project - $(buildConfiguration)' inputs: command: 'publish' projects: '**/*.csproj' publishWebProjects: false arguments: '--no-build --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)/$(buildConfiguration)' zipAfterPublish: true - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: drop' condition: succeeded()
Esta versión introduce la tarea de compilación
DotNetCoreCLI@2
.- task: DotNetCoreCLI@2 displayName: 'Run unit tests - $(buildConfiguration)' inputs: command: 'test' arguments: '--no-build --configuration $(buildConfiguration)' publishTestResults: true projects: '**/*.Tests.csproj'
Esta tarea de compilación ejecuta el comando
dotnet test
.Observe que esta tarea no especifica el argumento
--logger trx
que usó al ejecutar las pruebas manualmente. El argumentopublishTestResults
lo agrega automáticamente. Este argumento le indica a la canalización que genere el archivo TRX en un directorio temporal, accesible a través de la variable integrada$(Agent.TempDirectory)
. Además, publica los resultados de la tarea en la canalización.El argumento
projects
especifica todos los proyectos de C# que coinciden con "**/*.Tests.csproj". La parte "**" coincide con todos los directorios y la parte "*.Tests.csproj" coincide con todos los proyectos cuyo nombre de archivo termine con ".Tests.csproj". La ramaunit-tests
contiene solo un proyecto de prueba unitaria, Tailspin.SpaceGame.Web.Tests.csproj. Si se especifica un patrón, se pueden ejecutar más proyectos de prueba adicionales sin necesidad de modificar la configuración de compilación.
Inserción de la rama en GitHub
Aquí podrá insertar los cambios en GitHub y ver la ejecución de la canalización. Recuerde que actualmente está en la rama unit-tests
.
En el terminal integrado, agregue azure-pipelines.yml al índice, confirme los cambios e inserte la rama en GitHub.
git add azure-pipelines.yml git commit -m "Run and publish unit tests" git push origin unit-tests
Vea cómo Azure Pipelines ejecuta las pruebas
Aquí verá la ejecución de las pruebas en la canalización y, después, visualizará los resultados desde Microsoft Azure Test Plans. Azure Test Plans proporciona todas las herramientas que necesita para probar correctamente las aplicaciones. Puede crear y ejecutar planes de pruebas manuales, generar pruebas automatizadas y recopilar comentarios de las partes interesadas.
En Azure Pipelines, realice el seguimiento de la compilación a lo largo de cada uno de los pasos.
Verá que la tarea Run unit tests - Release (Ejecutar pruebas unitarias: versión) ejecuta las pruebas unitarias tal como lo hizo manualmente desde la línea de comandos.
Vuelva al resumen de la canalización.
Vaya a la pestaña Pruebas.
Verá un resumen de la serie de pruebas. Se han superado las cinco pruebas.
En Azure DevOps, seleccione Test Plans y, después, Runs (Ejecuciones).
Verá las series de pruebas más recientes, incluida la que acaba de ejecutar.
Haga doble clic en la serie de pruebas más reciente.
Verá un resumen de los resultados.
En este ejemplo, se han superado las cinco pruebas. Si se han producido errores en algunas de las pruebas, puede ir a la tarea de compilación para obtener más información.
También puede descargar el archivo TRX para examinarlo con Visual Studio u otra herramienta de visualización.
Aunque solo agregó una prueba, supone un buen punto de partida y se corrige el problema inmediato. Ahora, el equipo tiene un lugar en el que agregar más pruebas y ejecutarlas a medida que mejoran el proceso.
Fusión de la rama (“branch”) con la principal mediante combinación con “merge”
En un escenario real, si estuviera satisfecho con los resultados podría combinar la rama unit-tests
con main
, pero para una mayor brevedad se omitirá ese proceso por ahora.