Compartilhar via


Este artigo foi traduzido por máquina.

Execução de teste

Web Application solicitação-resposta teste com JavaScript

Dr. James McCaffrey

Baixe o código de exemplo.

Na coluna deste mês explicam como escrever automação de teste de solicitação-resposta baseado em navegador, simples e eficaz usando JavaScript. A melhor maneira de ver em que eu estou cabeças é dar uma olhada nas capturas de tela em de figuras 1 e 2. Figura 1 mostra um aplicativo ASP.NET Web simples mas representativo em teste chamado Search de produto. Um usuário insere alguns seqüência de pesquisa no controle de caixa de texto único do aplicativo e especifica se a pesquisa a ser executada de maneira sensível a maiúsculas e minúsculas usando dois controles de botão de rádio. Os resultados da pesquisa são exibidos em um controle de caixa de listagem.

Figura 1 do aplicativo em teste Search de produto

image: Product Search Web Application Under Test

Embora o aplicativo da Web de exemplo em teste é baseado em ASP.NET, a técnica apresentadas neste artigo pode ser usada para criar automação de teste para aplicativos Web escritos usando tecnologias de geração de página mais dinâmicas, incluindo PHP, JSP, CGI e outros.

Figura 2 mostra a automação de teste de solicitação-resposta em ação. Observe que a estrutura de automação de teste é baseado em navegador. Uma das vantagens da técnica que apresento aqui em comparação comparado abordagens alternativas é que a técnica pode ser usada com a maioria dos navegadores da Web principal e pode ser executada em máquinas de host de teste com a maioria dos sistemas operacionais.

Figura 2 de execução de teste de solicitação-resposta

image: Request-Response Test Run

A estrutura de automação de teste consiste em uma única página HTML que contém um conjunto relativamente pequeno de funções JavaScript. Observe que a primeira linha da saída de execução do teste indica que a automação de teste está usando a biblioteca jQuery. O equipamento lê dados de entrada caso de teste, que corresponde à entrada do usuário, e por meio de programação postagens que a entrada de dados para o aplicativo da Web Search de produto. O equipamento aceita os dados de resposta HTTP resultantes e examina essa resposta para um valor esperado para determinar um resultado de êxito/falha do caso de teste.

As seções deste artigo a seguir, primeiro faça uma breve descrevo o aplicativo Web sob teste mostrada no 1 Figura , para que você compreenda quais fatores são relevantes para testes de solicitação-resposta HTTP. Em seguida, explique em detalhes o teste estruturam o código mostrado executando em do Figura 2 para que você poderá modificar a estrutura para atender às suas próprias necessidades. Concluo com alguns comentários sobre quando baseada em navegador automação de teste de solicitação-resposta com o JavaScript é adequada e quando abordagens alternativas podem ser mais adequadas.

Este artigo pressupõe que você ter JavaScript de nível intermediário e a capacidade do ASP.NET, mas, mesmo se você for um iniciante com essas tecnologias deve ser capaz de acompanhar minhas explicações sem muita dificuldade.

Criando aplicativo da Web

Usei o Visual Studio 2008 para criar o aplicativo Web de pesquisa de produto em teste. A fim de aproveitar a capacidade do Visual Studio para configurar um site, selecionei o arquivo | novo | site da Web a partir da barra de menus principal. Em seguida, selecionei a opção Site vazio partir da caixa de diálogo Nova site da Web resultante. Eu especificou um local HTTP na minha máquina local para criar um site completo do ASP.NET, ao invés de especificar um local no sistema de arquivos para usar o servidor de desenvolvimento interno do Visual Studio. Selecionei o idioma translation from VPE for Csharp para meu código de lógica.

Depois de clicar em OK, o Visual Studio criou o site ProductSearch vazio. Na janela Solution Explorer, eu clicou com o botão direito do mouse no projeto ProductSearch e Add New Item selecionado no menu de contexto. Eu selecionado o item de formulário da Web e aceita o nome de página padrão de default.aspx e tiver clicado em Adicionar para gerar a página. Em seguida criei a interface do usuário simples para o aplicativo Web sob teste, conforme apresentada no do Figura 3.

Figura 3 Web App UI

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Product Search</title>
</head>
<body bgcolor="#ccbbcc">
  <form id="form1" runat="server">
  <div>
    <asp:Label ID="Label1" runat="server" Text="Find:" 
      Font-Names="Arial" Font-Size="Small">
    </asp:Label>  
    <asp:TextBox ID="TextBox1" runat="server" Width="114px">
    </asp:TextBox>  
    <asp:Button ID="Button1" runat="server" onclick="Button1_Click" 
      Text="Go" />
    <br />

    <asp:RadioButtonList ID="RadioButtonList1" runat="server" 
      Font-Names="Arial" Font-Size="Small">
    <asp:ListItem>Case Sensitive</asp:ListItem>
    <asp:ListItem Selected="True">Not Case Sensitive</asp:ListItem>
    </asp:RadioButtonList>
  </div>
  <asp:ListBox ID="ListBox1" runat="server" Height="131px" Width="246px"
    Font-Names="Courier New" Font-Size="Small">
  </asp:ListBox>
  </form>
</body>
</html>

Como explicarei daqui a pouco, quando a criação de automação de teste de solicitação-resposta HTTP você deve conhecer as IDs de qualquer entrada controles que você deseja simular a entrada do usuário no. Nesse caso eu tenha acesso a código-fonte do aplicativo em teste, mas, mesmo se você não tiver acesso ao código fonte você sempre pode determinar IDs usando a funcionalidade de modo de exibição de código-fonte de um navegador da Web de controle de entrada. Observe que os controles de botão de rádio de duas são na verdade, representados por um único controle de entrada com ID RadioButtonList1, em vez de dois controles como você deve ter adivinhado.

Adicionei a lógica do aplicativo diretamente no arquivo Defaut.aspx em vez de usar o mecanismo de code-behind. Na parte superior da página, criei um bloco de script para armazenar o código da lógica do aplicativo:

<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  // ...
</script>

Adicionei uma pequena classe no bloco de script para representar um objeto de produto:

public class Product {
  public int id;
  public string desc;
  public Product(int id, string desc) {
    this.id = id; this.desc = desc;
  }
}

Em seguida, adicionei um objeto ArrayList de escopo de aplicativo interno para simular um armazenamento de dados externos:

public static ArrayList data = null;

Em cenários de aplicativos Web mais realistas, armazenamentos de dados são geralmente externos, como um arquivo XML ou banco de dados do SQL Server. No entanto, ao executar solicitação-resposta HTTP de teste, o local de armazenamento de dados do aplicativo é irrelevante até certo ponto. A solicitação HTTP não tem conhecimento do local do armazenamento de dados e a resposta HTTP normalmente contém apenas HTML. Em seguida, adicionei alguns códigos para preencher o armazenamento de dados interno com itens de produto:

protected void Page_Load(object sender, EventArgs e) {
  if (!IsPostBack) {
    data = new ArrayList();
    Product p1 = new Product(111, "Widget");
    Product p2 = new Product(222, "Gizzmo");
    Product p3 = new Product(333, "Thingy");
    data.Add(p1); data.Add(p2); data.Add(p3);
  }
}

Por fim, coloquei toda a lógica de aplicativo para o manipulador de eventos para o Button1 evento, clique. Começo Limpar área de resultado ListBox1 e buscando a entrada do usuário:

ListBox1.Items.Clear();
string filter = TextBox1.Text.Trim();
string sensitivity = RadioButtonList1.SelectedValue;

A variável de seqüência de sensibilidade irá manter “ distinção de maiúsculas/minúsculas ” ou distinção de “ não maiúsculas/minúsculas. ”

Em seguida, coloque as informações de cabeçalho na área de resultado ListBox1 e declarar uma seqüência de caracteres para armazenar um resultado de pesquisa de produto e inicializar um contador para controlar quantos itens de produtos correspondem ao filtro de pesquisa:

ListBox1.Items.Add("ID   Description");
ListBox1.Items.Add("================");
string resultRow;
int count = 0;

Posso iterar por meio de cada objeto Product no armazenamento de dados de ArrayList para verificar se a seqüência de caracteres de filtro de pesquisa corresponde ao campo de descrição do objeto atual:

foreach (Product p in data) {
  resultRow = "";
  if (sensitivity == "Not Case Sensitive" &&
    p.desc.IndexOf(filter, 
    StringComparison.CurrentCultureIgnoreCase) >= 0) {
    resultRow = p.id + " " + p.desc; ++count;
  }
  else if (sensitivity == "Case Sensitive" && 
    p.desc.IndexOf(filter) >= 0) {
    resultRow = p.id + " " + p.desc; ++count;
  }
  if (resultRow != "") ListBox1.Items.Add(resultRow);
}

Para cada produto que corresponde ao filtro de pesquisa, eu construir uma seqüência de caracteres do resultado e incrementar o contador de visitas. Observe que o método IndexOf convenientemente está sobrecarregado para aceitar um argumento de diferenciação de maiúsculas e minúsculas.

A lógica do aplicativo termina, adicionando que uma linha em branco e um resumo de contagem em ListBox1 a área de exibição:

ListBox1.Items.Add("");
ListBox1.Items.Add("Found " + count + " matching items");

Para manter o tamanho do aplicativo da Web como pequeno e simples possível, eu tomaram muitas atalhos que não usados em um ambiente de produção. Em particular não forneci qualquer erro de verificação ou manipular.

Automação de teste de solicitação-resposta

Criei a página de equipamento de teste mostrada em execução na Figura 2 usando o bloco de notas. A estrutura geral do equipamento de é mostrada na do Figura 4.

Figura 4 do estrutura do conjunto de teste

<html>
<!-- RequestResponseTests.html -->
<head>
  <script src=’http://localhost/TestWithJQuery/jquery-1.3.2.js’>
  </script>
  <script type="text/javascript">
    $(document).ready(function() {
      logRemark("jQuery Library found and harness DOM is ready\n");
    } );
  
    var targetURL = ‘http://localhost/TestWithJQuery/ProductSearch/Default.aspx’;

    var testCaseData =
[ ‘001,TextBox1=T&RadioButtonList1=Case+Sensitive&Button1=clicked,333 Thingy’,
‘002,TextBox1=t&RadioButtonList1=Not+Case+Sensitive&Button1=clicked,Found 2 matching items’ ];
        
    function runTests() {
      try {
        logRemark(‘Begin Request-Response with JavaScript test run’);
        logRemark("Testing Product Search ASP.NET application\n");
        // ...
        logRemark("\nEnd test run");
      }
      catch(ex) {
        logRemark("Fatal error: " + ex);
      }
    }
    
    function getVS(target) {
      // ...  
    }

    function getEV(target) {
      // ...  
    }

    function sendAndReceive(target, rawVS, rawEV, inputData) {
      // ...  
    }

    function logRemark(comment) {
      // ...  
    }

  </script>
</head>
<body bgcolor="#66ddcc">
  <h3>Request-Response Test Harness Page</h3>
  <p><b>Actions:</b></p><p>
  <textarea id="comments" rows="24" cols=63">
  </textarea></p>
  <input type="button" value="Run Tests" onclick="runTests();" /> 
</body>
</html>

A estrutura de código da interface do usuário no elemento body na parte inferior da página consiste somente em um texto, um elemento textarea para exibir as informações e um botão para iniciar a automação de teste.

A estrutura de equipamento de teste começa, usando o atributo de src do elemento de script para fazer referência a biblioteca jQuery. A biblioteca jQuery é uma coleção de código-fonte aberto de funções JavaScript disponíveis na jquery.com. Embora jQuery foi criada com o desenvolvimento da Web em mente, a biblioteca contém funções que o tornam adequado para a automação de teste leve de solicitação-resposta. Aqui eu apontar para uma cópia local da versão 1.3.2 da biblioteca. Para fins de automação de teste, usando uma cópia local da biblioteca é mais confiável do que aponta para uma cópia remota. Em seguida, usarei o idioma de jQuery .ready $ (documento) para Certifique-se de que meu equipamento pode acessar a biblioteca e que o equipamento de DOM é carregado na memória.

Depois de configurar um targetURL variável que aponta para o aplicativo Web sob teste, disco rígido que o código internos casos de teste separado por vírgulas em uma matriz de seqüência de caracteres denominada testCaseData. Aqui tenho apenas dois casos de teste, mas em um ambiente de produção, você pode ter centenas de casos. Dados do caso de teste externas geralmente é preferível dados do caso de teste interno como os dados externos podem ser modificados e compartilhada com mais facilidade. No entanto, como a técnica que eu estou apresentando aqui é leve, dados do caso de teste interno são uma opção de design razoável.

O primeiro campo em um caso de teste é um número de identificação do caso. O segundo campo é brutos solicitar dados para enviar para o aplicativo em teste. O terceiro campo é um resultado esperado.

Como eu sabia o formato dos dados solicitação? A maneira mais fácil para determinar o formato dos dados de solicitação HTTP é executar experimentação preliminar com o aplicativo em teste examinando dados de solicitação real usando uma ferramenta de agente de log HTTP como o Fiddler.

Executando testes

A função de controle principal harness é denominada runTests. A função runTests usa um mecanismo de nível superior try-catch para fornecer tratamento de erros rudimentar. Uso uma função auxiliar chamada logRemark para exibir informações para o elemento textarea harness. A estrutura usa getVS de funções do auxiliar e getEV para obter os valores ViewState e EventValidation atuais do aplicativo em teste da Web do ASP.NET. Esses valores gerados pelo aplicativo codificado na Base64 atuam principalmente como mecanismos de segurança e de estado e devem ser enviadas como parte de qualquer solicitação HTTP POST. A função sendAndReceive realiza a solicitação HTTP real e retorna a resposta HTTP correspondente. A função runTests itera em cada caso de teste:

for (i = 0; i < testCaseData.length; ++i) {
  logRemark("==========================");
  var tokens = testCaseData[i].split(‘,’);
  var caseID = tokens[0];
  var inputData = tokens[1];
  var expected = tokens[2];
  ...

Uso interno dividir função para separar cada seqüência de caso de teste em partes menores. Em seguida eu chamo o getVS e getEV funções auxiliares:

logRemark(‘Case ID     : ‘ + caseID); 
logRemark(‘Fetching ViewState and EventValidation’);
var rawVS = getVS(targetURL);
var rawEV = getEV(targetURL);

O loop de processamento principal continua chamando a função sendAndReceive e examinando a resposta HTTP resultante para o valor esperado do caso de teste associado:

var response = sendAndReceive(targetURL, rawVS, rawEV, inputData);
logRemark("Expected    : ‘" + expected + "’");
if (response.indexOf(expected) >= 0)
  logRemark("Test result : **Pass**");
else if (response.indexOf(expected) == -1)
  logRemark("Test result : **FAIL**");
} // main loop

A função de auxiliar getVS depende da biblioteca jQuery:

function getVS(target) {
  $.ajax({
    async: false, type: "GET", url: target,
    success: function(resp) {
      if (resp.hasOwnProperty("d")) s = resp.d;
      else s = resp;
         
      start = s.indexOf(‘id="__VIEWSTATE"’, 0) + 24;
      end = s.indexOf(‘"’, start);
    }
  });
  return s.substring(start, end);
}

A idéia principal da função getVS é enviar uma solicitação GET priming para o aplicativo em teste, a resposta de busca e analisar o valor ViewState. A função de .ajax $ aceita uma função anônima. O assíncrono, tipo e parâmetros de URL devem ser bastante auto-explicativo. O método hasOwnProperty(“d”) do objeto resp resposta é essencialmente um mecanismo de segurança presente no Microsoft .NET Framework 3.5 e não é necessário nessa situação.

Eu extrair o valor ViewState, procurando o início do atributo e, em seguida, mais de 24 caracteres para onde o valor ViewState começa realmente de contagem. O código de função getEV é exatamente o mesmo que o getVS de código, exceto pelo fato de que o valor EventValidation começa 30 caracteres da identificação inicial = atributo EVENTVALIDATION. Tendo getVS separado e funções getEV oferece flexibilidade, mas requer duas solicitações priming separado. Uma alternativa é Refatorar getVS e getEV em uma função auxiliar simples.

A função de auxiliar sendAndReceive executa a solicitação HTTP real e a resposta resultante de busca. A função começa convertendo bruto ViewState e EventValidation seqüências de caracteres em strings codificadas por URL e, em seguida, constrói os dados para enviar para o aplicativo da Web:

function sendAndReceive(target, rawVS, rawEV, inputData) {
  vs = encodeURIComponent(rawVS);
  ev = encodeURIComponent(rawEV);
  postData = inputData + ‘&__VIEWSTATE=’ + vs +
    ‘&__EVENTVALIDATION=’ + ev;
  ...

A função interna encodeURIComponent codifica os caracteres que não são valores válidos nos dados de postagem em uma seqüência de escape. Por exemplo, o ‘ / ’ caractere é codificado como % 2F. Após uma mensagem de log, o sendAndReceive utiliza o método de .ajax $ para criar uma solicitação HTTP POST:

logRemark("Posting " + inputData);
$.ajax({
  async: false,
  type: "POST",
  url: target,
  contentType: "application/x-www-form-urlencoded",
  data: postData,
  ...

O método de .ajax $ foi criado principalmente para enviar solicitações XML HTTP assíncronas, mas definindo o parâmetro async como false, o método pode ser usado para enviar solicitações síncronas padrão. Elegantes! Você pode considerar o valor do parâmetro de tipo de conteúdo como uma seqüência de caracteres magic simplesmente significa que os dados postados em um elemento HTML form. A função sendAndReceive usa o mesmo padrão como getVS capturar a resposta HTTP associada:

success: function(resp, status) {
      if (resp.hasOwnProperty("d")) s = resp.d;
      else s = resp;
    },
    error: function(xhr, status, errObj) {
      alert(xhr.responseText);
    }
  });
  return s;
}

Posso também usar o parâmetro de erro opcionais para exibir quaisquer erros fatais em uma caixa de alerta.

A função final no equipamento de teste é o utilitário logRemark:

function logRemark(comment) {
  var currComment = $("#comments").val();
  var newComment = currComment + "\n" + comment;
  $("#comments").val(newComment);
}

Uso jQuery seletor e encadeamento de sintaxe para obter o texto atual no elemento textarea, que tem uma identificação de comentários. O ‘ # ’ sintaxe é usada para selecionar um elemento HTML pela identificação e a função val pode atuar como um valor setter e getter. Eu acrescentar o valor do parâmetro de comentário e um caractere de nova linha no texto de comentário existente e use sintaxe jQuery para atualizar o elemento textarea.

Alternativas

A alternativa principal para o baseada em navegador, abordagem da linguagem JavaScript que apresentei neste artigo é criar uma estrutura baseado em shell usando uma linguagem such as translation from VPE for Csharp. Em comparação com uma abordagem baseada em shell, a abordagem baseada em navegador é mais útil quando você estiver trabalhando em um ambiente altamente dinâmico em que a automação de teste tem um tempo de vida curto. Além disso, a abordagem baseada em navegador apresentada aqui é bastante independente de plataforma. A técnica funcionará com qualquer navegador e combinação de sistema operacional que oferece suporte a JavaScript e a biblioteca jQuery.

Dr. James McCaffrey* trabalha para a Volt Information Sciences Inc., onde gerencia o treinamento técnico para engenheiros de software que trabalham na Microsoft. Ele trabalhou em vários produtos da Microsoft, incluindo o Internet Explorer e o MSN Busca. Dr. McCaffrey é o autor de* .NET Test Automation Recipes(Apress, 2006) e pode ser contatado pelo jmccaffrey@volt.com ou v-jammc@microsoft.com.