Desarrollar una aplicacion siguiendo la metodología de eXtreme Programming (moderno) considerando las siguientes prácticas:
- BDD / TDD
- CI / CD
- Ensemble programming (aka "Pair/Mob programming")
- Otros: Infra as Code, Feature Flags, Monitoring, etc.
El stack de tecnología a utilizar es el siguiente:
- Runtime
- .NET 9
- SQL Server
- Docker (runtime)
- Render
- Uptime Robot
- Desarrollo
- Gitlab (backlog, repo, ci/cd, etc)
- Visual Studio / Rider / Co-Pilot
---
I. Visión de proyecto y Kick-off
El "negocio" del desarrollo
- En general el desarrollo de software implica resolver problemas de negocio.
Visión de negocio
- abc
Mínimo Producto Viable
- Registración
- Lista de expertos
II. Diseño de la solución
<arquitectura alto nivel>
Hexagonal Architecture
III. Planning y Bootstrap
https://blog.nicopaez.com/2013/12/19/project-bootstrap-como-inicio-mis-proyectos/
IV. Walking Skeleton
Notas:
- Comenzar con un walking skeleton que nos permita establecer las bases de la arquitectura de la aplicación de punta a punta.
- Que ese walking skeleton este cubierto por una prueba de aceptación end-to-end.
- Incluir como parte de ese walking skeleton el proceso de versionado, build, test y deploy a un ambiente simil producción en un esquema de integración continua.
Pasos:
- Crear solución:
- dotnet new sln --name <nombre solucion>
- Crear proyecto usando plantilla MVC dentro de la solución:
- dotnet new mvc --auth None --no-https --framework net8.0 -o WebApp
- dotnet sln add WebApp/WebApp.csproj
- dotnet add WebApp/WebApp.csproj package Npgsql
- dotnet build
- Generar .gitignore
- El archivo .gitignore sirve para indicarle a Git qué archivos o carpetas debe ignorar al hacer seguimiento de cambios en un repositorio.
- Generar Build Script
- Un build script en un archivo .gitlab-ci.yml define el conjunto de acciones que GitLab CI/CD debe ejecutar durante la etapa de construcción de tu pipeline. Generalmente, incluye una lista de comandos que se ejecutan para compilar tu código, ejecutar pruebas o preparar tu aplicación para el despliegue.
- No es recomendable poner código en el build script porque no lo podemos testear localmente. Lo recomendable es colocar el código en scripts.
- How to create file execute mode permissions in Git on Windows?
- Instalar Linter
- Cuando se trabajando con distintos SO y diferentes IDE se debería agregar un EditorConfig en el proyecto. Este sirve para colocar las convenciones a seguir en el desarrollo. Ver ejemplo de archivo EditorConfig.
- Luego, tenemos que configurar nuestro IDE para que "entienda" las convenciones ubicadas en el EditorConfig. En Visual Studio tenemos que habilitar la opción "Code Style".
- Cuando se suba el código al respositorio se tiene que asegurar que se siguen dichas convenciones. Linter hace esto. Parsea tu código y asegura que tu código cumple con las convenciones del lenguaje.
- En .NET el comando dotnet format aplica automáticamente las reglas de estilo definidas en .editorconfig y mantener tu código limpio y consistente.
- Dentro del pipeline se debería hacer una verificación (sin modificación) de que el código está correctamente formateado según las reglas del archivo .editorconfig. Para esto usamos el comando dotnet format --verify-no-changes
- Definir el arnés de pruebas
- Un arnés de pruebas (en inglés, test harness) es un conjunto de herramientas, código y configuraciones que se utilizan para automatizar la ejecución de pruebas en una aplicación o componente de software.
- En un desarrollo guiado por pruebas, el objetivo no es escribir las pruebas. Las pruebas son un medio para escribir la aplicación. Estas pruebas tempranas suelen ser distintas a las que uno hace a posteriori del desarrollo con el objetivo de testear. Si no tienes en cuenta las pruebas de manera temprana terminarás solo haciendo pruebas de caja negra. Esto es costoso.
- ¿Que tipos de pruebas haremos en nuestra arquitectura?:
- Aceptación: Deben ser end-to-end (e2e), expresadas en términos de la aplicación. Estas pruebas testean la aplicación a nivel de historia de usuario (tipo caja negra), son lentas y no dan buen feedback. Son pruebas que las puede escribir el usuario, por tanto están escritas en un lenguaje natural. Herramientas: nunit + Gherkin + Reqnroll + Selenium.WebDriver. Reqnroll ejecutará el Gherkin. Por debajo, usaremos el Selenium.WebDriver para automatizar tareas en el navegador.
- Unitaria: Prueban la lógica de negocio sin dependencia de infraestructura. Herramientas: nunit + moq
- Integración: Probar como se integra código que escribiste tú con código que tú no escribiste. Herramientas: nunit
- Agregar a la solución un proyecto de test NUnit (con pruebas e2e) para probar la Web.
- Sintaxis Gherkin para especificar los tests
- Usar Reqnroll para interpretar Gherkin en C#
- Con el WebApplicationFactory vamos a instanciar un objeto que va a representar a la aplicación web dentro del proceso que corre los tests sin necesidad de levantar la aplicación por fuera.
- Usar la librería FluentAssertions la cual extiende los métodos de aserción tradicionales (Assert.Equal, Assert.True, etc.) y permite escribir pruebas más naturales y fáciles de leer.
- Configurar la medición de cobertura
- Aplicación de BDD
- En un enfoque tradicional, tenemos al analista que habla con el usuario y genera una especificación (un documento, con casos de uso por ejemplo), luego el developer codifica y finalmente el tester define y ejecuta casos de prueba.
- En un enfoque de testing ágil / XP, Usuario, Dev y Tester se juntan para armar una especificación basada en ejemplos. Estos ejemplos son casos de prueba. El Dev a construir teniendo a mano los casos de prueba, es decir, ya sabe como se va a testear la funcionalidad que está desarrollando. Luego el Tester ejecuta los mismos casos de prueba que el Dev utilizó para guiarse en el desarrollo (esto ahorra muchas idas y vueltas). Para que el usuario pueda trabajar colaborativamente en la definición de estos casos de prueba se utilizan una herramienta como Gherkin. Luego se utilizan otra serie de herramientas para convertir Gherkin a código. Reqnroll ejecutará el Gherkin. Por debajo, usaremos el Selenium.WebDriver para automatizar tareas en el navegador. La idea entender los requerimientos a partir de ejemplos concretos los cuales se construyen en conjunto con el usuario. A partir de herramientas como Reqnroll y Cucumber podemos automatizar los requerimientos expresados en Gherkin. Cada escenario representa un test. Selenium.WebDriver es un paquete que maneja Google Chrome. Esto requiere que esté instalado el Chrome. Si queremos ejecutar esto dentro de Gitlab, vamos a necesitar crear una imagen de Docker que tenga .NET y Google Chrome para que esto pueda correr dentro del Pipeline (Crear imagen custom docker para usar en el CI)
- BDD implica entender los requerimientos a partir de ejemplos concretos.
- User stories are typically broken down into acceptance criteria or business rules. Focusing on examples makes the intention of these rules clear—each rule should be illustrated by one or more examples.
- Escribir un escenario (test) nos ayude a recorrer los puntos centrales de la arquitectura
- Completar la idea de Walking Squeleton
- En Gherkin @wip sirve para excluir escenarios. Luego configurar tu runner para no ejecutarlas: > dotnet test --filter "TestCategory!=wip"
- Armar arnés de prueba en NET
- Armar una configuración de compose que levanta 2 bases de datos. Una BD para cuando ejecute la aplicación de forma manual y otra BD para usarla en el contexto de las pruebas automatizadas.
- Levantar BDs: > docker compose up
- Puedes acceder a las bases de datos mediante el Management Studio.
- Para las pruebas ¿por qué no usar una BD en memoria? Poque necesitamos usar un entorno lo más parecido a producción, para mitigar riesgos.
- Cuando ejecutemos las pruebas, tenemos que apuntar a la BD de pruebas a través del Connection String. Los archivos appsettings son dinámicos por ambiente.
- Ejecutar los Tests (es importante indicar la variable de ambiente de test para no pegarle a la base de datos de desarrollo)
- > set ASPNETCORE_ENVIRONMENT=test
- > dotnet test
- > dotnet test --filter "TestCategory!=wip"
- La BD se creará a través de Migrations (scripts que crean la estructuras de tablas).
- > Add-Migration InitialCreate
- >
- Crear proyecto Web.Test el cual tendrá tests desde la perspectiva del usuario escritos en lenguaje Gherkin.
- En la clase Hooks de Reqnroll,
- En el método BeforeTestRun() se crea instancia de la aplicación
- En el meétodo BeforeScenario() me conecto a la BD y borro todo para que los escenarios estén limpios. Luego levanto el Chrome Driver para que en cada escenario usar una instancia del navegador distinta.
Nota: A esta altura deberíamos tener la aplicación con su arnés de pruebas, además de un prueba básica que nos sirvió para armar el esquelo (levantar la aplicación web, que reciba un request, va hasta la base de datos y vuelve). Si bien es cierto que esta prueba básica es de una funcionalidad que no tiene utilidad para el usuario, pero si para nosotros, para mitigar riesgos respecto a la arquictura.
Ciclo BDD
Luego empezamos a desarrollar la aplicación:
- Historia de usuario >> Criterios de aceptación (reglas de negocio) >> Escenarios (Ejemplos)
- Podemos tener los siguientes escenarios:
- Camino feliz
- Caminos alternativos
- El escenario feliz seguramente va a tener una prueba de aceptación end-to-end. En el caso de los escenarios alternativos tenemos que evaluar cuales tendrán sus pruebas e2e.
- Por tanto, arrancamos por el escenario feliz.
Container Docker
- Compilación:
- > docker build --target final -t hikeplanner .
- Ejecutar:
- > docker run --rm -ti --entrypoint /bin/sh hikeplanner
- > ls -l /app
- Ver redes levantadas: > docker network ls
- Ejecutar:
- > docker run -ti -e ASPNETCORE_ENVIRONMENT=production -v "$(pwd)/HikePlanner.Web/appsettings.Development.json":/app/appsettings.json --network hikeplanner_default --entrypoint /bin/sh hikeplanner
- Levanta un contenedor Docker basado en la imagen llamada hikeplanner. Dentro del contenedor se ejecuta tu aplicación, con ciertas configuraciones adicionales:
- Windows: > docker run -ti -e ASPNETCORE_ENVIRONMENT=production -v "C:\DATA\_PROYECTOS\hikeplanner\HikePlanner.Web\appsettings.Development.json:/app/appsettings.json" --network hikeplanner_default -p 8080:8080 hikeplanner
- Windows: > docker run -ti -e ASPNETCORE_ENVIRONMENT=production -v "/c/DATA/_PROYECTOS/hikeplanner/HikePlanner.Web/appsettings.Development.json:/app/appsettings.json" --network hikeplanner_default -p 8080:8080 hikeplanner
- Linux: > docker run -ti -e ASPNETCORE_ENVIRONMENT=production -v "$(pwd)/HikePlanner.Web/appsettings.Development.json":/app/appsettings.json --network hikeplanner_default -p 8080:8080 hikeplanner
host.docker.internal
Recursos de aprendizaje
- Introducción a Test-Driven Development (Udemy)
- Una experiencia de Extreme Programming Moderno (Udemy)
- Unit Testing en C# (Udemy)
- Growing Object Oriented Software Guided by Tests - Steve Freeman (Book)
- Selenium with C# .NET (.NET 8 and C# 12)
- Estilos de TDD: un voto para London
- Definición de Estrategia de Pruebas
- BDD with ReqnRoll