Muy buenas,
En la mayoría de los proyectos, además de los “Unit Test”, siempre existen un conjunto de pruebas que es necesario llevar a cabo a fin de validar que nuestro entorno se ejecutará de manera correcta y ya no sólo en un entorno, si no más de uno, concretamente entornos como “Integración” y “Preproducción”.
Rara vez, los “Mock” son creados, otras veces, los “Unit Test” consumen servicios WCF con url distintas dependiendo del entorno, las buils funcionan para un entorno pero no para el otro porque los Endpoint no tiene la url correcta, etc., y así sucesivamente… Y por si fuera poco, a la hora de “la puesta en producción” siempre surge algún imprevisto,… ¡Cuantas veces hemos visto esto!
En el día a día, llevar a cabo estos test siempre cuesta: El ”copy + paste”, en ocasiones hasta podemos llegar a ser meros “autómatas”: Búsqueda en Google, copio, modifico, ejecuto, etc..
En fin, creo que esto ya lo conocemos todos y no voy a entrar en la manera de hacer los test puesto que ya existen infinidad de Best Practices, recomendaciones, ejemplos, etc. sobre ello. En lo que si quiero entrar es en los “Load Test” o Tests de Carga, a los que yo, particularmente también llamaría “Integration Test” y a continuación veréis por qué:
Supongamos una aplicación Web que consume servicios WCF:
- Creamos un proyecto de tipo “Test” y le añadimos al nombre, el sufijo “.IntegrationTest”. En este proyecto creamos todos los tests necesarios para probar nuestro aplicativo.
- Como sabrás, en proyectos web tenemos la opción del menú contextual “Add Config Transform”, cosa que no ocurre en los proyectos de tipo test. Por tanto, editamos el fichero de proyecto “.csproj” (previa acción: “Unload Project”) e incluimos en nuestro “.config” las líneas de la 6 a la 13 según el siguiente “code snippet”:
1: <ItemGroup>
2: ...
3: <None Include="App.config">
4: <SubType>Designer</SubType>
5: </None>
6: <None Include="App.Integracion.config">
7: <DependentUpon>App.config</DependentUpon>
8: <SubType>Designer</SubType>
9: </None>
10: <None Include="App.PreProduccion.config">
11: <DependentUpon>App.config</DependentUpon>
12: <SubType>Designer</SubType>
13: </None>
14: ...
15: </ItemGroup>
- En la misma ruta física donde se encuentre el fichero App.config creamos dos ficheros con los nombre “App.Integracion.config” y “App.PreProduccion.config” con el siguiente contenido inicial:
1: <?xml version="1.0"?>
2: <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
3: <system.web>
4: </system.web>
5: </configuration>
Nota: El echo de incluir este namespace en nuestros “.configs”, nos permite tener la transformación adecuada con respecto al fichero original “App.config”. Toda la información a estas transformaciones puedes encontrarla en este link.
- Guardamos los ficheros y cargamos (“Reload Project”) nuevamente el proyecto en Visual Studio, veremos lo siguiente:
- En este punto, necesitamos crear las diferencias de configuración de cada uno de los entornos en el “.config” adecuado. El “App.config” contendrá el contenido común a la configuración de ambos entornos.
- Ejemplo para un EndPoints:
1: <system.serviceModel>
2: <client xdt:Transform="Insert">
3: <endpoint address="https://localhost/Service1.svc"
4: binding="customBinding" bindingConfiguration="HttpBinaryBinding"
5: contract="IService1" name="CustomBinding_Service1"/>
6: </client>
7: </system.serviceModel>
- Cada de transformación tendrá la configuración particular de cada entorno.
- Para que todo funcione, y nuestros tests prueben íntegramente los servicios dependiente del entorno, es necesario volver a incluir las siguientes líneas en nuestro “.csproj” (“.IntegrationTest”)" al final del mismo y antes de los cierre de tags </target></Project>:
1: <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)MicrosoftVisualStudiov10.0WebMicrosoft.Web.Publishing.Tasks.dll" />
2: <Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
3: <!-- Generate transformed app config in the intermediate directory -->
4: <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
5: <!-- Force build process to use the transformed configuration file from now on. -->
6: <ItemGroup>
7: <AppConfigWithTargetPath Remove="app.config" />
8: <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
9: <TargetPath>$(TargetFileName).config</TargetPath>
10: </AppConfigWithTargetPath>
11: ItemGroup>
12: </Target>
- Ahora sólo nos queda configurar el “click” de cambio de entorno. Para ello, creamos dos nuevas “Solution Cofiguration”::
- Finalizado este punto, ya estamos en disposición de lanzar nuestros test en nuestros dos entornos simplemente cambiando el “Configuration Name”
Llegado este punto, podemos incluir en nuestras buils estos test a fin de que nuestro sistema quede totalmente integrado. Pero, ¿Donde están los “Load Test” ? Pues bien, bastará con que :
- Añadamos a nuestro proyecto de test un nuevo item “Load Test…”
- Añadamos los test a chequear (“.IntegrationTest”) en la ventana de “Edit Test Mix”
- Y, todo listo, a analizar los resultados:
Hasta este punto, hemos realizado un integración completa probando la lógica de presentación, desde el “Presenter” en un patrón MVP o desde el “Controler“ en en el patrón MVC y siempre, con un desacoplamiento del contexto “Web”. Ventaja
Si ahora incluimos en nuestro proyecto test de tipo ”Web Performance Test”, ya tenemos la integración completa incluida nuestras páginas .ASPX. Eso sí, para seguir haciendo uso de “Un sólo click por entorno”:
- Generamos el código a partir de Web Test.
- Y configuramos la URL en nuestros “.config”
1: WebTestRequest request1 = new WebTestRequest("http://localhost:49570/default.aspx");
2: request1.ThinkTime = 5;
3: request1.Method = "POST";
4: FormPostHttpBody request1Body = new FormPostHttpBody();
5: request1Body.FormPostParameters.Add("__VIEWSTATE", "/wEPDwUKLTQ1NTczNzkyNGRkTZU93NCSzcCZ5pxELADnsc9UqyEWuBmSoft7FRTH4es=");
6: request1Body.FormPostParameters.Add("__EVENTVALIDATION", "/wEWBQL86K7VBQLR49OXDQKm1J7DAwKB3audDALT8MqYCCqSOYhduF9mB1R1c58WGZTcWirhoI3Guwfza" +
7: "hrT86tK");
8: request1Body.FormPostParameters.Add("ctl00$MainContent$TextBox1", "1");
9: request1Body.FormPostParameters.Add("ctl00$MainContent$btnSync", "Sync");
10: request1.Body = request1Body;
11: yield return request1;
12: request1 = null;
Nota: Para la ejecución de los “Load Test” y a fin de que la ejecución de estos test de carga se asemeje a la carga de nuestra aplicación en el entorno de Producción, recomiendo dividir los “Web Performance Test” por funcionalidad concreta, de manera que el reparto de carga (en la ventana “Edit Test Mix”) se acerquen más a la carga real. Es decir, podríamos generar “Web Performance Test” por operación a realizar por el usuario, básicamente, podríamos asemejarlos a los test que hemos generado para la integración de nuestro sistema con los Servicios WCF.
Es muy importante llevar a cabo los Test de Carga para cada una de nuestras aplicaciones, principalmente, para poder estimar el dimensionamiento de la infraestructura que dará soporte a nuestra aplicación y no encontrarnos sorpresas o imprevistos no contemplados.
Nuestros “Integration Test” y la ejecución de test por entornos garantiza en cualquier momento y a un sólo click de ratón que nuestro aplicativo esta esta en perfecto estado. Recomiendo estos tests en cualquier caso, pero sin lugar a dudas, en aquellos casos en los que las “Buils” no puede ser usadas por algún motivo “inexplicable” !!!
Saludos
@JuanluElGuerre