Pego este snippet por si a alguno le sirve. Lo he escrito a raiz de la necesidad de realizar una prueba de carga sobre una página aspx de una manera rápida y gratis.
Por si alguno no conoce qué es Watin, dejo un enlace: http://watin.sourceforge.net/
Watin es un framework gratuito que permite automatizar el navegador. Se basa en Watir que es otro framework que hace lo mismo pero en Ruby. Inicialmente sólo soportaba la automatización de Internet Explorer, pero actualmente funciona también con Firefox.
Un getting started para watin: http://watin.sourceforge.net/gettingstarted.html
Pues bien, resulta que la mayor complicación a la hora de usar Wating para realizar una prueba de carga, reside en que Watin necesita que todos los hilos que inician una ventana del navegador se ejecuten en un “Apartment” independiente.
Un Apartment en el mundo Windows es un contenedor lógico dentro de la memoria que impide que se compartan datos entre hilos. Es un mecanismo de seguridad que evita que se produzcan Memory Leaks (pérdida de datos) y bloqueos.
El Framework de .net no usa Apartments y todos los objetos manejados son responsables de gestionar los mecanismos de proporciona la plataforma para evitar los problemas que resuelve el uso de Apartments.
Debido a que las COM si usa Apartments el CLR necesita porder crear e inicializar un Apartment cuando trabaja en un entorno de “Interoperabilidad“.
La gestión de los Apartments en .net la proporciona la propiedad ApartmentState de la clase System.Threading.Thread (http://msdn.microsoft.com/en-us/library/system.thr…)
Manos a la obra. Primero, creraremos el código que ejecutará la petición, mediante Watin de la página en cuestión.
/// Este es el método que llamaremos en cada hilo que ejecute la pruebas de carga. /// Debe cumplir la firma del delegado System.Threading.ParameterizedThreadStart public void LoadTest(object param) { string sParam = (string)param; IE ie = new IE("http://www.google.es"); ie.TextField(Find.ByName("q")).TypeText(sParam); ie.Button(Find.ByName("btnG")).Click(); ie.Close(); }
Este código abre una ventana del navegador, navega hasta la página de google, realiza una búsqueda y se cierra.
Luego, nos falta un bucle para lanzar n hilos que ejecuten esta petición.
for (int i = 0; i < 15; i++) { // Delegado al método a ejecutar ParameterizedThreadStart ts = new ParameterizedThreadStart(LoadTest); // instancia del hilo Thread newThread = new Thread(ts); // Muy importante para que Watin se ejecute correctamente: // Indicamos aquí que el hilo debe ejecutarse eun un "Apartment" independiente newThread.SetApartmentState(ApartmentState.STA); // lanzar el hilo (le pasamos un parámetro cualquiera) newThread.Start(@"1+" + i.ToString()); }
Y ya está! si ejecutamos este código, veremos abrir el navegador 15 veces y ejecutar la petición.
Lo refinamos un poquito. Podemos por ejemplo, añadir código para especificar un timeout, y que el hilo principal espere a que todos los demás acaben.
Lo primero que necesitaría, es guardar la referencia a todos los hilos que voy creando en el bucle. Me creo por tanto una lista de hilos lanzados.
// Lista de hilos lanzados List<Thread> launchedThreads = new List<Thread>();
Y con esto, me creo un méodo de espera, que no hará otra cosa que esperar a que todos los hilos lanzados acaben.
/// Espera a el tiempo especificado a que los hilos de la lista /// hayan acabado de ejecutarse private void WaitForComplete(List<Thread> launchedThreads, int timeOut) { // La hora a la que he comenzado a esperar DateTime startTime = DateTime.Now; // Tiempo transcurrido desde que empezado a esperar hasta la hora que // evalúo el timeout TimeSpan elapsedTime = DateTime.Now.Subtract(startTime); // Me indica si debo seguir esperando bool keepWating; do // bucle de espera { keepWating = false; foreach (Thread t in launchedThreads) { // Si hay al menos un hilo en ejecución seguimos esperando if (t.ThreadState != ThreadState.Stopped) { Thread.Sleep(100); elapsedTime = DateTime.Now.Subtract(startTime); keepWating = true; break; } } } while (keepWating && elapsedTime.TotalMilliseconds < timeOut); }
Entonces, lo que deberé hacer será, llamar a este método a continuación del bucle.
// Esperamos a que acaben de ejecutar todas las pruebas. // El timeout se especifica en milisegundos. WaitForComplete(launchedThreads, 60000);
Y ya está. Es bastante impresionante ver lanzar 50 ventanas del navegador de una vez. Eso sí, hace falta un porrón de memoria para que la cosa vaya medianamente bien (no es mi caso).
Y… un último detalle. Lo añadimos todo a una proyecto, añadimos referencia al NUnit y ya lo tenemos listo para ejecutar desde una consola de pruebas.
Código para descargar. http://cid-da2e56199efa5596.skydrive.live.com/self.aspx/BlogAttachments/WatinLoadTest.zip
Crossposted from crisfervil.com
