Compartir a través de


Este artículo proviene de un motor de traducción automática.

Ejecución de prueba

Pruebas con F # de solicitud-respuesta

James McCaffrey

Descarga de código disponible en la MSDN Code Gallery
Examinar el código en línea

Contenido

La aplicación en la prueba
Solicitud-respuesta ASP.NET pruebas con F #
Detalles de instrumento
Ajustar arriba

Lenguaje de programación de F # tiene varias características que resulte adecuado para la automatización de prueba de software. En la columna de este mes, mostraré cómo utilizar F # para realizar pruebas de aplicaciones Web ASP.NET de respuesta de solicitud HTTP. Específicamente, cree un programa de instrumento de prueba breve que simula un usuario ejerce una aplicación ASP.NET. Mediante programación, el instrumento de F # envía información de solicitud HTTP a la aplicación en un servidor Web. A continuación, recupera la secuencia de respuesta HTTP y examina el texto HTML de un valor esperado de algún tipo para determinar un resultado correcto o incorrecto. Junto a ser una técnica útil pruebas en su propio derecha, aprender a realizar pruebas con F # de respuesta de solicitud HTTP proporciona una forma excelente de aprender acerca del lenguaje de F #. Esta columna se supone que tiene familiaridad básica con tecnología ASP.NET y conocimientos de programación de .NET intermedios con C# o VB.NET, pero no asume que tiene experiencia con el idioma de F #. Sin embargo, incluso si son nuevas en ASP.NET y automatización de prueba en general, todavía podrá seguir mes este sin demasiada dificultad. Para ver mi objetivo, tener un aspecto atFigures 1 y 2figura 1 ilustra el ejemplo de aplicación Web ASP.NET en la prueba que utilizar. El sistema en la prueba es una aplicación Web simple pero representativa, denominada MiniCalc.

fig01.gif

Figura 1 Prueba ApplicationUnder de Web ASP.NET

Deliberadamente guardar mi aplicación Web ASP.NET sometida a prueba tan sencillo como sea posible para no oculte los puntos claves de la automatización de prueba. Aplicaciones Web realistas son considerablemente más complejas que el ficticio MiniCalc aplicación se muestra en la figura 1 , pero las pruebas de las técnicas que se describen aquí fácilmente F # generalizar a las aplicaciones complejas. La aplicación MiniCalc Web acepta dos enteros y una instrucción para agregar o multiplicar. Después envía los valores a un servidor Web donde se calcula el resultado. El servidor crea la respuesta HTML y lo envía al explorador cliente donde el resultado se muestra con cuatro decimales. la figura 2 muestra un instrumento de prueba F # en acción.

fig02.gif

Figura 2 solicitud-respuesta pruebas con F #

Mi instrumento de F # se denomina project.exe y no acepta argumentos de línea de comandos. Por motivos de simplicidad, dispongo de información codificada incluyendo la dirección URL de la aplicación Web, entrada de prueba y resultados de la prueba espera. Explicaré cómo se puede parametrizar el instrumento de prueba más tarde. Mi agente empieza por la dirección URL de destino de localhost:15900/MiniCalc/Default.aspx de eco. Observe que Estoy utilizando el servidor de Web de desarrollo de Visual Studio en lugar de IIS, para especificar la un número de puerto (15900) en lugar del puerto predeterminado 80 de IIS. En caso de prueba 001, mi instrumento de F # envía mediante programación información que corresponde a un usuario escribe 5 en el control TextBox1, escribiendo un 3 en TextBox2, seleccionando RadioButton1 (para indicar una operación de adición) y haciendo clic en Button1 (para calcular). En segundo plano, el instrumento de captura de la respuesta HTTP desde el servidor Web y, a continuación, busca la respuesta de una indicación que 8.0000 (el resultado correcto de 5 + 3) está en el control de resultado TextBox3. Las pistas de instrumento el número de prueba casos que pase y el número que produzca un error y (que es una operación sorprendentemente interesante en F #) y muestra los resultados una vez procesados todos los casos de prueba.

En las siguientes secciones de esta columna, se describen brevemente la aplicación Web sometida a prueba para saberlo exactamente lo que se está probando. A continuación, le guiarán a través de los detalles de creación automatización ligera de solicitud-respuesta HTTP mediante el lenguaje de F #. Envolver describiendo cómo puede modificar las técnicas que he presentado a sus propias necesidades. También presento unas opiniones acerca de por qué debe tomar tiempo para investigar F #. Creo que encontrará la información presentada aquí interesante y una adición útil a pruebas el conjunto de herramientas.

La aplicación en la prueba

Observemos el código de la aplicación Web de ASP.NET MiniCalc, que es el destino de mi automatización de prueba. Se creó la aplicación de MiniCalc utilizando Visual Studio 2008 para aprovechar el servidor Web de desarrollo integrado. Después de iniciar Visual Studio, hace clic en archivo | nuevo | sitio Web. Fin de evitar el mecanismo de código subyacente de ASP.NET y conservar todo el código de mi aplicación Web en un archivo único, seleccionó la opción sitio Web vacío. Para evitar el uso de IIS, seleccionó la opción de sistema de archivos del control de lista desplegable de campo de ubicación. Decidí usar C# para la aplicación MiniCalc, pero el instrumento de prueba F # presento en esta columna funciona con aplicaciones ASP.NET escritas en VB.NET. Además, con pequeñas modificaciones el instrumento puede destinar otro tipo de .NET aplicaciones escritas mediante tecnologías como ASP clásico, CGI, PHP, JSP, Ruby y así sucesivamente. Dado que va a utilizar el servidor de desarrollo de Visual Studio, especifica una ubicación estándar en el sistema de archivos, tales como C:\MyWebApps\MiniCalc, en lugar de una ubicación específica de IIS, por ejemplo, C:\Inetpub\wwwroot\MiniCalc. Hice clic en Aceptar en el cuadro de diálogo nuevo sitio Web para generar la estructura de mi aplicación Web. A continuación, pasó a la ventana Explorador de soluciones, hizo clic con el botón secundario del mouse en el nombre del proyecto MiniCalc y seleccione Agregar nuevo elemento en el menú contextual. He seleccionado Web Form de la lista de plantillas instaladas y acepta el nombre de archivo default.aspx. He desactivado la opción "Colocar código en un archivo independiente" y, a continuación, haga clic en el botón Agregar.

Doble clic a continuación, en el nombre de archivo default.aspx en el Explorador de soluciones para modificar el código generado por la plantilla. Elimina todo el código de plantilla y lo reemplaza por el código que se muestra en la figura 3 .

Aplicación Web MiniCalc de la figura 3 en Probar origen

<%@ Page Language="C#" %>
<script language="C#" runat="server">
  private void Button1_Click(object sender, System.EventArgs e)
  {
    int alpha = int.Parse(TextBox1.Text.Trim());
    int  beta = int.Parse(TextBox2.Text.Trim());

    if (RadioButton1.Checked) {
      TextBox3.Text = Sum(alpha, beta).ToString("F4");
    }
    else if (RadioButton2.Checked) {
      TextBox3.Text = Product(alpha, beta).ToString("F4");
    }
    else
     TextBox3.Text = "Select method";
  }
  private static double Sum(int a, int b) {
    double ans = a + b;
    return ans;
  }
  private static double Product(int a, int b) {
    double ans = a * b;
    return ans;
  }
</script>

<html>
  <head>
    <style type="text/css">
      fieldset { width: 16em }
      body { font-size: 10pt; font-family: Arial }
    </style>
    <title>Default.aspx</title>
  </head>
  <body bgColor="#ffcc99">
    <h3>MiniCalc by ASP.NET</h3>
    <form method="post" name="theForm" id="theForm" 
      runat="server" action="Default.aspx">
      <p><asp:Label id="Label1" runat="server">
        Enter integer:&nbsp&nbsp</asp:Label>
      <asp:TextBox id="TextBox1" width="100" runat="server" /></p>
      <p><asp:Label id="Label2" runat="server">
        Enter another:&nbsp</asp:Label>
      <asp:TextBox id="TextBox2" width="100" runat="server" /></p>
      <p></p>
      <fieldset>
        <legend>Arithmentic Operation</legend>
        <p><asp:RadioButton id="RadioButton1" GroupName="Operation"
          runat="server"/>Addition</p>
        <p><asp:RadioButton id="RadioButton2" GroupName="Operation"
          runat="server"/>Multiplication</p>
        <p></p>
      </fieldset>
      <p><asp:Button id="Button1" runat="server" text=" Calculate "
        onclick="Button1_Click" /> </p>
      <p><asp:TextBox id="TextBox3" width="120" runat="server" /></p>
    </form>
  </body>
</html>

Para mantener mi código de origen de pequeño tamaño y fácil de entender, no Estoy utilizando buenas prácticas de codificación de esta aplicación Web. En concreto, no hace ninguna comprobación de errores y uso un enfoque de diseño algo desordenado combinando los controles de servidor (como <asp:TextBox>) con HTML sin formato (como <fieldset>). Las partes más importantes de la lista en la figura 3 para poder Nota de código son los identificadores de Mis controles de servidor ASP.NET. Utilizar predeterminado Label1 identificadores (símbolo de usuario), TextBox1 y TextBox2 (entrada de dos enteros), RadioButton1 y RadioButton2 (opción de suma o multiplicación) Button1 (calcular) y TextBox3 (resultado). Para realizar automatizada solicitud-respuesta HTTP pruebas para una aplicación ASP.NET, debe conocer los identificadores de controles de la aplicación. En esta situación, tengo el código fuente disponible porque voy a crear la aplicación mí; pero incluso si está probando una aplicación Web que no escrito, siempre puede examinar la aplicación mediante la funcionalidad Ver código fuente de un explorador Web.

Para comprobar que mi aplicación Web sometida a prueba se generó correctamente, presione la tecla <f5>. Hice clic en Aceptar en el cuadro de depuración no habilitada diálogo para indicar a Visual Studio para modificar el archivo Web.config de la aplicación Web. A continuación, inicia el servidor de desarrollo Visual Studio y asignó un número de puerto aleatorio a la aplicación Web, en este caso 15900, como puede ver en la figura 1 . Si hubiera deseado especificar un número de puerto, podría ha seleccionado el proyecto MiniCalc en el Explorador de soluciones y a continuación, hace clic en el elemento de menú principal de ver y había seleccionado la opción de la ventana Propiedades. Esto mostrará una opción "Usar puertos dinámicos" que se establece en un valor predeterminado de True, y podría cambiar el valor en false y escriba un valor en el campo "Número de puerto".

Observe que se establece el atributo de acción de mi elemento <form> a default.aspx. En otras palabras, cada vez que un usuario envía una solicitud, se ejecuta el mismo código de página default.aspx. Esto proporciona mi aplicación Web MiniCalc la el funcionamiento de una sola aplicación en lugar de una secuencia de diferentes páginas Web. HTTP es un protocolo sin estado, ASP.NET realiza el efecto de aplicación manteniendo el estado de la aplicación Web en un tipo de valor oculto especial, denominado ViewState. Como se verá en breve, trabajar con ViewState una aplicación ASP.NET es la clave a datos a la aplicación de contabilización mediante programación.

Solicitud-respuesta ASP.NET pruebas con F #

Ahora que se ha visto la aplicación Web sometida a prueba, repasemos el F # instrumento de programa de prueba que generó la captura de pantalla en la figura 2 . F # provisionalmente está programado que suministran con la versión siguiente de Visual Studio. Para esta columna, decidí usar septiembre de 2008 versión de Community Technical Preview (CTP) de F # y Visual Studio 2008. En el momento de que leer esta columna, puede tener otras opciones para utilizar F #. En cualquier caso, usé la versión CTP era muy estable y he instalado sin ninguna dificultad. El proceso de instalación de F # coloca todo lo que necesario para escribir programas F # en Visual Studio.

Inicio iniciar Visual Studio y haciendo clic en archivo | nuevo | proyecto. En el cuadro de diálogo nuevo proyecto, había seleccionado el tipo de proyecto de F #, selecciona la plantilla F# Application para crear una aplicación de línea de comandos. Denominé mi proyecto, "." El nombre de proyecto se convertirá en el nombre del ejecutable resultante (aquí, project.exe) para un nombre más descriptivo, como solicitud-respuesta-agente, podría haber sido preferible. Después de especificar una ubicación para mi proyecto F # y haga clic en Aceptar, hizo doble clic en archivo Program.fs en el Explorador de soluciones de Visual Studio para abrir una ventana de edición. La estructura general de mi instrumento de F # aparece en la figura 4 .

Figura 4 prueba F # agente estructura

#light
open System
open System.Text
open System.Net
open System.IO
open System.Web

printfn "\nBegin F# HTTP request-response test of MiniCalc Web App\n"
let url = "http://localhost:15900/MiniCalc/Default.aspx"
printfn "URL under test = %s \n" url

// define function to get ViewState & EventValidation
// set up test case data
try
  // set numPass & numFail counters to 0
  // iterate and process each test case
  // display number pass & number fail
  printfn "\nEnd F# test run"
  Console.ReadLine() |> ignore
with
  | :? System.OverflowException as e ->
    printfn "Fatal overflow exception %s" e.Message
    Console.ReadLine() |> ignore
  | :? System.Exception as e ->
    printfn "Fatal: %s" e.Message
    Console.ReadLine() |> ignore

// end source

La primera línea en mi código de origen F #, #light, indica al compilador F # utilizar sintaxis ligero, donde nuevas líneas y la sangría en la parte determinan la estructura de programa. Esto es práctica estándar para programas de F #. Uso la F # palabra abrir clave para poner los correspondientes espacios de nombres .NET en el ámbito, similar a utilizar la palabra clave en C# o la palabra clave importaciones en Visual Basic. Observe que puesto que estoy utilizando sintaxis claro, no se utilizan un terminador de instrucción explícita como C#, JavaScript y otros lenguajes de C utilizado el carácter de punto y coma. Se abrir System.Text para poder usar fácilmente la clase Encoding para convertir texto en bytes. Las clases claves necesarias para registrar mediante programación datos a una aplicación Web de centro de los espacios de nombres System.NET y System.IO. El espacio de nombres System.Web no es visible de forma predeterminada para una aplicación de F #, por lo que agregué explícitamente ese espacio de nombres haciendo clic con el botón secundario del mouse en el nombre del proyecto en Visual Studio y seleccionando la opción Agregar referencia. System.Web abrirá para que puede utilizar la clase HttpUtility para realizar la codificación URL. La siguiente instrucción es sencillo:

printfn "\nBegin F# HTTP request-response test of MiniCalc Web App\n"

La función de biblioteca printfn() muestra información en el shell de comandos como cabría esperar. Las cadenas en F # están delimitadas por comillas dobles y pueden contener secuencias de escape incrustadas como \n para una nueva línea. Una de las características clave de F # es que casi todo lo manipula funciones, y la mayoría de las funciones devuelven un valor que debe procesarse de forma explícita con. Sin embargo, la función printfn() no devuelve un valor. A continuación, establecí mi dirección URL de destino:

let url = "http://localhost:15900/MiniCalc/Default.aspx"

Utilizar la palabra clave permiten y el operador = para enlazar un valor de cadena a un identificador denominado dirección url. (Muchos miembros del equipo F # prefieren utilizar el símbolo de término en lugar de identificador.) Observe el número de puerto en la cadena de dirección URL. En este caso, no debe especificar el tipo de datos para la dirección url de identificador por lo que el compilador F # tendrá que inferir el tipo. Podría haber explícitamente escrito identificador url como una cadena:

let url : string = " . . . "

En lugar de codificar la dirección URL en el origen de F #, podría recuperar como argumento de línea de comandos utilizando la matriz Sys.argv integrada:

let url = Sys.argv.[1]

Sys.argv.[0] es el nombre de programa,. [1] es el primer argumento y así sucesivamente. A continuación eco de la dirección URL de destino:

printfn "URL under test = %s \n" url

La función printfn() utiliza formato, donde %s indica una cadena de estilo de lenguaje. Otros especificadores comunes incluyen %d para %x de hexadecimal, entero, %.2f de punto flotante con dos decimales y un %a genérica para todos los tipos. F # admite el control de excepciones utilizando una construcción try con. Si se produce una excepción en el bloque try, el control se transferirá a la con el bloque donde se controlará la excepción. Observe que con sintaxis luz que no uso comenzar llaves para definir y bloquear puntos finales de un código. En su lugar, todas las instrucciones que se aplica sangría el mismo número de espacios en blanco (no se puede utilizar caracteres de tabulación) forman parte del mismo bloque de código. La última línea en el bloque try es una llamada a ReadLine(), para que mi instrumento se detener la ejecución y mantener el shell de comandos en la pantalla:

Console.ReadLine() |> ignore 

ReadLine() devuelve una cadena y en F #, se debe considerar el valor devuelto o el compilador generará un error. Aquí uso el | > operador de canalización para enviar el valor devuelto al objeto omitir integrados. Como alternativa para descartar un valor devuelto en la mayoría de las situaciones, puede utilizar el identificador especial _ (subrayado) similar al siguiente:

let _ = Console.ReadLine()

Sin embargo, por motivos de sintaxis de sutiles F #, este enfoque no funciona como la última instrucción de un bloque de código. Mi código de control de excepciones utiliza una construcción de F # interesante:

| :? System.OverflowException as e ->
    printfn "Fatal overflow exception %s" e.Message
| :? System.Exception as e ->
    printfn "Fatal: %s" e.Message

Se puede interpretar la primera parte de este código significa "Si la excepción coincide con tipo System.OverflowException, a continuación, asignar la excepción a un identificador denominado e e imprima un mensaje de cadena que incluye el texto del mensaje de excepción." El | símbolo (token) es el operador de coincidencia. En otras palabras, si se produce una excepción, el programa intenta hacer coincidir la excepción con dos patrones. ¿El:? pruebas de operador para tipos en lugar de valores.

Detalles de instrumento

Ahora que he explicado la estructura general de mi instrumento de prueba F #, echemos un vistazo a los detalles. Si hace referencia a la figura 4 , verá que defina una función que recupera información de ViewState y EventValidation desde la aplicación MiniCalc sometida a prueba. En F #, debe definir funciones en código fuente antes de que se llama a. Como que se describió anteriormente, HTTP es un protocolo sin estado, ASP.NET utiliza un valor de ViewState especial para mantener el estado. Un valor de ViewState es una cadena codificada en Base64 que representa el estado de la aplicación Web ASP.NET después de cada solicitud-respuesta de acción de ida y vuelta. Un valor de EventValidation es algo similar a un valor de ViewState, pero se utiliza por motivos de seguridad para ayudar a evitar ataques de inserción de secuencias de comandos. Según parece, si desea enviar mediante programación a una aplicación Web ASP.NET, debe enviar valor ViewState actual de la aplicación y valor actual de EventValidation. El código para la función que busca valores de ViewState y EventValidation aparece en la figura 5 .

Función de la figura 5 para búsqueda ViewState y EventValidation

let getVSandEV (url : string) =
  let wc = new WebClient()
  let st = wc.OpenRead(url)
  let sr = new StreamReader(st)
  let res = sr.ReadToEnd()
  sr.Close()
  st.Close()

  let startI = res.IndexOf("id=\"__VIEWSTATE\"", 0) + 24 
  let endI = res.IndexOf("\"", startI)
  let viewState = res.Substring(startI, endI - startI)

  let startI = res.IndexOf("id=\"__EVENTVALIDATION\"", 0) + 30 
  let endI = res.IndexOf("\"", startI)
  let eventValidation = res.Substring(startI, endI - startI)
  (viewState, eventValidation)

Observe que use la palabra clave permiten y el operador = para definir una función F #. GetVSandEV mi función el nombre y especifique un solo parámetro de entrada denominado dirección url que tiene el tipo de cadena. Mi firma de función indicar explícitamente el tipo de valor devuelto, pero podría termine, tal como explicaré en breve. Mi función comienza creando instancias de un objeto WebClient:

let wc = new WebClient()

Debido a que encuentra una instrucción open al espacio de nombres System.NET que aloja la clase WebClient, no necesito que calificar totalmente el nombre de la clase. A continuación, se enviar una solicitud de desbloquear a la dirección URL de destino:

let st = wc.OpenRead(url)
let sr = new StreamReader(st)
let res = sr.ReadToEnd()

Uso del método OpenRead(), combinado con un objeto StreamReader y su método ReadToEnd(), para enviar una solicitud a la aplicación Web de destino, tomar todo el origen HTML de la respuesta como una cadena y enlazar ese resultado a un identificador denominado res. En la mayoría de los casos, se sugiere escribir explícitamente F # los identificadores, tales como permiten res: cadena = sr.ReadToEnd(), pero en esta situación, permite el compilador de F # inferir el tipo de identificador res. En este momento, es necesario analizar los valores de ViewState y EventValidation enlazado con el identificador de res del valor de cadena. Es similar la parte de valor de cadena enlazado a res que tiene el valor de ViewState:

<input type="hidden" name="__VIEWSTATE"
 id="__VIEWSTATE" value="/wEPDwUK . . . Zmv==" />

En primer lugar, buscar la ubicación de comienzo del valor de ViewState mediante el método String.IndexOf():

let startI = res.IndexOf("id=\"__VIEWSTATE\"", 0) + 24 

El argumento significa comenzar la búsqueda res índice de 0 posición 0, que es el principio de la cadena de respuesta. Observe que, puesto que mi cadena de destino contiene caracteres de comillas dobles, delimitan la cadena de destino utilizando un \ "secuencia de escape. Una vez que se encuentra donde comienza el valor del objetivo, el valor de ViewState real comienza 24 caracteres de ese índice. Tenga en cuenta que ", VIEWSTATE" tiene dos caracteres de subrayado inicial, no sólo uno. Se trata de forma bastante vulgar a analizar para el valor inicial de ViewState y hace que mi código brittle. Sin embargo, en esta situación, estoy realizando automatización de prueba rápido y sencillo y estoy dispuesto a aceptar la posibilidad de que puede interrumpir mi código. Ahora buscar la ubicación dentro de res donde el valor de ViewState termina:

let endI = res.IndexOf("\"", startI)

Puedo buscar la primera aparición de cualquier carácter de comillas dobles después el principio del valor ViewState acaba de encontrar (en startI) y enlaza esa ubicación con identificador endI. Ahora que sabe donde el valor de ViewState empieza y termina en res de cadena de respuesta, puede extraer el valor mediante el método SubString:

let viewState = res.Substring(startI, endI - startI)

Los dos argumentos al método SubString() son el índice de res para empezar a extraer de y el número de caracteres para extraer (no el índice para finalizar la extracción, que es un error común). Como siempre con una indización de métodos, tenemos mucho cuidado para evitar inicial o final un carácter demasiado pronto o demasiado tarde. El viewState identificador contiene ahora el valor de ViewState inicial para la aplicación Web ASP.NET de destino. Extraer el valor de EventValidation sigue el mismo modelo:

let startI = res.IndexOf("id=\"__EVENTVALIDATION\"", 0) + 30 
let endI = res.IndexOf("\"", startI)
let eventValidation = res.Substring(startI, endI - startI)

Observe que reutilizar Mis identificadores startI y endI y enlazarlos a nuevos valores. Esto es legal en F # cuando el código está dentro de una definición de método, tal como está aquí, pero no válida en el código de nivel superior fuera de un método. En este punto, tiene mis dos valores enlazados a identificadores de ViewState y EventValidation. Uso una característica de F # ordenada para eficazmente ambos valores devueltos simultáneamente:

(viewState, eventValidation)

No debe utilizar una palabra clave return explícita; la última línea de una función F # representa automáticamente el valor devuelto. Aquí uso paréntesis para construir una tupla de F #, que se puede considerar como un conjunto de valores. Como mencioné anteriormente, se podría ha definido la función getVSandEV() de forma que se indica explícitamente el valor devuelto:

let getVSandEV (url : string) : (string * string) = . . .

El (cadena * cadena) notación significa que la función devuelve una tupla de dos cadenas. Ahora que he definido mi función auxiliar, configuro Mis datos de prueba:

let testCases =
  [| "001,TextBox1=5&TextBox2=3&Operation=RadioButton1" +
     "&Button1=clicked,value=\"8.0000\",Add 5 and 3"
     "002,TextBox1=5&TextBox2=3&Operation=RadioButton2" +
     "&Button1=clicked,value=\"15.0000\",Multiply 5 and 3"
     "003,TextBox1=0&TextBox2=0&Operation=RadioButton1" +
     "&Button1=clicked,value=\"0.0000\",Add 0 and 0"
  |]

Aquí creo inmutable F # matriz denominada testCases que contiene tres cadenas. El [|. . . notación |] utiliza F # para delimitar una matriz. Como Mis tres cadenas son bastante largas, descomponer cada cadena en dos partes y utilizar el operador de concatenación de cadenas a panorámica ellos volver +. F # también acepta el ^ carácter para la concatenación de cadenas. La primera parte de la primera cadena en matriz testCases, 001, es un identificador de caso de prueba. Después de un delimitador de coma, tengo TextBox1 = 5 & TextBoxt2 = 3 que, cuando registra la aplicación MiniCalc Web, simulará un usuario escribe estos valores. El tercer par de nombre de valor (operación = RadioButton1) es una manera de simular un usuario selecciona un elemento del control RadioButton, en este caso, el RadioButton que corresponde a la adición. Habrá incorrectamente adivinado (como originalmente ha) en algo parecido a RadioButton1 = activada. Sin embargo, RadioButton1 es un valor del control de operación, no un control en Sí. El par de nombre y valor cuarto (Button1 = hace clic en) es un poco engañoso. Es necesario proporcionar un valor para Button1 para simular que se ha hecho clic un usuario, pero funcionará cualquier valor. Por lo tanto, podría haber utilizado "Button1 = yadda"o incluso simplemente"Button1 =" si hubiera quisiera. Pero "Button1 = hace clic en" es más descriptivo. El siguiente campo (valor = "8.0000") en mi caso de prueba de datos son un valor esperado en el formulario de una cadena que va buscar en la secuencia de respuesta HTML. El campo final (agregar 5 y 3) es un comentario simple.

El uso de una matriz de F # para almacenar los datos de prueba es quizás el enfoque más sencillo, pero hay varias alternativas. Podría haber reemplazado el [|. . . delimitadores de |] corchetes, como: permiten testCases = [001". . ." ] Para crear una lista de F #. Las listas son comunes y temático con anteriores lenguajes de programación funcionales como LISP y prólogo, pero en esta situación, no debería obtener ninguna ventaja mediante una lista. Como alternativa, podría haber creado una matriz mutable F # como esto:

let testCases = Array.create 3 ""
testCases.[0] <- "001,. . ."
testCases.[1] <- "002,. . ."
testCases.[2] <- "003,. . ."

El uso de matrices mutables es bastante raro en F # y, de nuevo, no debería obtener ninguna ventaja mediante uno aquí; Mencione la posibilidad de principalmente para mostrar la sintaxis de matriz mutable. Si se hace referencia volver a la estructura de instrumento de prueba general se muestra en la figura 3 , verá que ahora estoy listo para comenzar a procesar cada caso de prueba. Comienzo configurando los identificadores de contador para almacenar el número de casos de prueba que pasar y producirá un error:

let numPass = ref 0
let numFail = ref 0

En la mayoría de los casos, debería simplemente vincula estos identificadores en 0, por ejemplo, "permiten numPass = 0". Sin embargo, necesito inicializar los contadores fuera una función y aumentar cada contador dentro de la función (como se verá en breve), pero imprime los valores finales fuera de la función. Esto hace que un pequeño problema debido de ámbito de los problemas de visibilidad. En situaciones extrañas como ésta, una consiste en utilizar la palabra clave ref como he hecho aquí. Explicaré cómo trabajar con los identificadores de ref breve. Ahora viene la parte más complicada de sintaxis de mi instrumento de prueba F #. Procesan cada caso de prueba:

testCases |>
  Seq.iter(fun (testCase : string) ->
  // parse current test case
  // get ViewState and EventValidation
  // send HTTP request and get response
  // check response for expected value
  // print pass or fail
  )

Utilizar el | > operador de canalización para enviar mi matriz testCases en el método Seq.iter(), que procesará cada elemento de prueba de la matriz. Seq.iter() espera un argumento que es una función que se aplicará a cada elemento de la secuencia. Podría escribir explícitamente una función, pero es un enfoque temático de F # definir una función anónima sobre la marcha con la palabra clave "fun". Mi función anónima acepta un parámetro de cadena único denominado prueba, que se enlazará a cada elemento en Activar en la matriz testCases. Aquí, la prueba de parámetro debe ser la cadena de tipo porque cada elemento de la matriz testCases es una cadena, lo que podría haber omitido la escritura explícita para la prueba. Dentro de mi función anónima, analizar los valores en el caso de prueba actual:

let delimits = [|',';'~'|];
let tokens = testCase.Split(delimits)
let caseID = tokens.[0]
let input = tokens.[1]
let expected = tokens.[2]
let comment = tokens.[3]

Observe utilizar F # [|. . sintaxis de |] con un delimitador de punto y coma para especificar una matriz de caracteres como un argumento para el método String.Split(). A continuación, llame a la función getVSandEV() que definí anteriormente:

let (vs, ev) = getVSandEV url 

Recuerde que getVSandEV() acepta un argumento de cadena único y devuelve una tupla de dos cadenas. Uso paréntesis para capturar la tupla, deconstruir los valores de tupla y enlazarlos a identificadores de vs y ev. Observe que en F #, no necesito utilizar paréntesis al llamar a una función definida por el programa. A continuación, generar mi datos POST real:

let data = input +
 "&__VIEWSTATE=" + HttpUtility.UrlEncode(vs) +
 "&__EVENTVALIDATION=" + HttpUtility.UrlEncode(ev)
let buffer = Encoding.ASCII.GetBytes(data)

Tomar el valor enlazado al identificador de entrada y concatenar los valores de ViewState y EventValidation necesarios. Dado que los valores de ViewState y EventValidation son codificada Base64, pueden contener caracteres que no son válidos en una solicitud HTTP POST (en concreto, = caracteres de relleno), por lo que utilizar el método UrlEncode() en el espacio de nombres System.Web para convertir cualquier dichos caracteres problemáticos en una secuencia de escape como % 3D. Uso el método GetBytes() para convertir la cadena en una matriz de bytes. Ahora puedo crear un objeto de solicitud HTTP:

let req = WebRequest.Create(url) :?> HttpWebRequest
req.Method <- "POST"
req.ContentType <- "application/x-www-form-urlencoded"
req.ContentLength <- int64 buffer.Length

He creado una instancia un objeto de HttpWebRequest mediante un mecanismo de fábrica de la clase WebRequest. Debe convertir el valor devuelto del método Create(), por lo que utilizar la conversión en tipos inferiores:?> operador, que puede interpretar significa "y convertir como escriba xxx". Puesto que las propiedades de mi objeto .NET son mutables, utilizar el <-operador para asignar valores de "POST", "aplicación/x-www-form-urlencoded" y buffer.Length a las propiedades de método, ContentType y ContentLength del objeto de solicitud. Observe que para convertir la propiedad de longitud del búfer de identificador como int64, use la sintaxis similar a lenguajes basados en C en lugar de utilizar el:?> operador. En F #, utilice la sintaxis de conversión de estilo C con tipos de valor. NET, tales como int64 y utilizar:?> con tipos de referencia, como HttpWebRequest. Con objeto de solicitud creado, puede desencadenar se a la aplicación Web sometida a prueba:

let reqSt = req.GetRequestStream()
reqSt.Write(buffer, 0, buffer.Length)
reqSt.Flush()
reqSt.Close()

El método Write() no escribe realmente su argumento de matriz de bytes, para que se llama explícitamente al método Flush() para ello. Ahora puede recuperar la respuesta HTTP y enlazarlo a un identificador denominado html:

let res = req.GetResponse() :?> HttpWebResponse
let resSt = res.GetResponseStream()
let sr = new StreamReader(resSt)
let html = sr.ReadToEnd()

En F #, al llamar a un método .NET que acepta un solo argumento, puede omitir los paréntesis en la llamada. Sin embargo, los métodos de .NET con sin argumentos, aquellos con dos o más argumentos y las llamadas de constructor todas requieren paréntesis. Por lo tanto, el equipo de F # sugiere utilizar siempre paréntesis al llamar a métodos de .NET. Ahora mostrar mi entrada de prueba:

printfn "============================"
printfn "Test case: %s" caseID
printfn "Comment  : %s" comment
printfn "Input    : %s" input
printfn "Expected : %s" expected 

Y, a continuación, examine la respuesta HTML para buscar la cadena esperada:

if html.IndexOf(expected) >= 0 then
  printfn "Pass"
  numPass := !numPass + 1
else
  printfn " ** FAIL **"
  numFail := !numFail + 1

Uso el método String.IndexOf(), que devuelve -1 si no se encuentra su argumento de cadena o la ubicación del argumento índice si se encuentra el argumento. Observe que en F #, si. . sintaxis, a continuación, utiliza una explícita y palabra clave. Recuerde que declaran Mis identificadores dos contador utilizando la palabra clave ref. Hemos visto que F # utiliza = para enlazar un valor inicial a un identificador y el <-operador para actualizar un identificador mutable. Este código muestra que F # utiliza la: = (operador) para cambiar el valor hace referencia a un identificador. También, observe que puesto que estoy trabajando con objetos de ref, use el! operador de eliminación de referencias y obtener el valor asociado al objeto ref. Después de cada caso de prueba se ha procesado, pausa para presionar una tecla y, a continuación, retrasa la ejecución de 1 segundo:

let _ = Console.ReadLine()
System.Threading.Thread.Sleep(1000)
) // end anonymous function

Aquí uso la expresión F # comunes para descartar el valor devuelto de Console.ReadLine () asignando el retorno a la special_identifier. En su lugar, podría ha canalizar la devolución de "Omitir", tal como expliqué anteriormente. Observe que porque no pudo abrir espacio de nombres System.Threading, totalmente debe conceder mi llamada al método Thread.Sleep(). Después prueba todas las entradas en la prueba de la matriz se ha procesado por la función anónima en Seq.iter(), el control se transfiere al final del instrumento de F # y se pueden imprimir resultados de resumen:

printfn "Number pass = %d" !numPass
printfn "Number fail = %d" !numFail
printfn "\nEnd F# test run"

Ahora está listo para ejecutar mi instrumento. Una vez que asegúrese de que el servidor de Web de desarrollo de Visual Studio se está ejecutando, puede ejecutar mi instrumento desde un shell de comandos, tal como se muestra en la figura 2 .

Ajustar arriba

El instrumento de ejemplo F # que he presentado aquí debe proporcionarle una base sólida para crear un instrumento de prueba cumple su propia situación determinada de pruebas. En general, el problema más difícil que se enfrentará es determinar qué pruebas de datos para exponer en la aplicación Web en. Una buena manera de hacerlo es manualmente ejercicio de la aplicación Web y capturar datos de solicitud HTTP con una herramienta rastreador para ver qué información se están enviando al servidor Web que aloja la aplicación. Hay muchas herramientas disponibles en Internet como descargas gratuitas.

Permitirme dirección por qué que desea investigar el lenguaje de F #. Después de todo, cualquier nuevo lenguaje de programación de aprendizaje requiere una una gran inversión de tiempo. Aprendizaje F # requerirá cierto esfuerzo, especialmente si es nuevo en programación funcional. Encontrará cuatro razones, que decidí aprender F #. En primer lugar, F # ha quedado muy buenas revisiones técnicas informales de algunas de mis colegas cuyas opiniones respetar. En segundo lugar, nunca duele para agregar una nueva tecnología a su conjunto de herramientas y reanudar. En tercer lugar, creo que un nuevo lenguaje de programación de aprendizaje le ayuda a comprender mejor otros lenguajes y utilizarlas de forma más eficaz. En cuarto lugar, estoy buscar el lenguaje de F # interesante y sin formato agradable obtener información.

Confirmación: Mis gracias a Tim Ng, provocar un jefe de desarrollo en el equipo F #, que proporciona mucho consejos técnicos para este artículo.

Envíe sus preguntas y comentarios para James a TestRun@Microsoft.com.

Dr. James McCaffrey trabaja en Volt Information Sciences, Inc., donde encarga de formación técnica los ingenieros de software en el campus de Redmond, Washington, de Microsoft. Ha trabajado en varios productos de Microsoft incluidos Internet Explorer y MSN Search. James es autor de .NET Test Automation Recipes (Apress, 2006). Juan puede ponerse en jmccaffrey@volt.como v-jammc@microsoft.com.