Este artigo foi traduzido por máquina.

Execução de teste

Automação de teste da interface do usuário na Web com o controle WebBrowser

James McCaffrey

Baixe o código de exemplo

James McCaffreyNa coluna deste mês mostro a você uma nova maneira de criar a automação de teste da interface do usuário para aplicativos da Web. A técnica que apresento fornece uma solução para um cenário de teste muito comum, mas complicado: como lidar com caixas de mensagem modal geradas por um aplicativo da Web.

A melhor maneira de ver o que eu estou direcionamento é observar as capturas de tela em do figuras 1 e 2 do . A imagem no do Figura 1 mostra um aplicativo da Web simples mas representativo hospedado no Internet Explorer. O aplicativo aceita entrada do usuário em uma caixa de texto e, depois que o usuário clica no botão Click Me, o aplicativo identifica a cor do item de entrada, e em seguida, exibe o resultado em uma segunda caixa de texto.

image: Example Web App Under Test

Figura 1 do exemplo de aplicativo da Web em teste

Observe que quando o Click Me botão for clicado, a lógica do aplicativo verifica se a caixa de entrada do usuário está vazia. Em caso afirmativo, ele gera uma caixa de mensagem modal com uma mensagem de erro. Lidar com uma caixa de mensagem é problemática em parte porque a caixa de mensagem não é parte do navegador ou o documento do navegador.

Agora vejamos o exemplo de teste execução em Figura 2. A estrutura de teste é um aplicativo Windows Forms. Incorporado dentro do Windows Forms aplicativo é um controle WebBrowser que permite a Windows Forms para exibir e manipular o aplicativo Web fictício em teste.

image: Example Test Run

De execução de teste de exemplo, a Figura 2

Se você examinar as mensagens no controle ListBox no final da estrutura do Windows Forms, você verá que a estrutura de teste começa por carregar o aplicativo Web sob teste no controle WebBrowser. Em seguida, o equipamento de usa um thread separado de execução para observar — e lidar com — qualquer mensagem caixas geradas pelo aplicativo da Web. Equipamento de simula um clique do usuário do aplicativo da Web Click Me botão, que por sua vez, cria uma caixa de mensagem de erro de janela restrita. O thread de observadores localiza a caixa de mensagem e simula um usuário clicar em imediatamente. A estrutura de teste termina, simulando um usuário digitar “ rosas ” na primeira caixa de entrada, clicando no Click Me botão e procurando o caso de teste espera-se a resposta de “ vermelho ” na segunda caixa de texto.

Neste artigo, descrevo brevemente o aplicativo Web sob teste de exemplo. Eu, em seguida, mostre você o código de estrutura de teste do Windows Forms para que você poderá modificar o meu código para atender aos seus cenários de testes. Concluo, descrevendo as situações em que essa técnica é aplicável e, quando técnicas alternativas podem ser melhores.

Este artigo pressupõe que você tenha conhecimentos básicos de desenvolvimento da Web e o intermediário c# técnicas de codificação, mas mesmo se você é iniciante em c# deve ser capaz de acompanhar sem muita dificuldade. Eu acho que você encontrará a técnica que apresento aqui uma adição útil para seus testes de software de pessoal, kit de ferramentas de desenvolvimento e gerenciamento.

O aplicativo em teste

Let’s dê uma olhada no código para o aplicativo da Web de exemplo que é o destino da minha automação de teste. Para simplificar, criei o aplicativo usando o bloco de notas. A funcionalidade do aplicativo é fornecida por JavaScript do lado do cliente, em vez de fazê-lo de processamento do lado do servidor. Como explicarei posteriormente, essa técnica de automação de teste que irá funcionar com aplicativos baseados na maioria das tecnologias da Web (como o asp.net, Perl/CGI e assim por diante), mas a técnica é mais adequada para aplicativos que usam o JavaScript para gerar as caixas de mensagem. Todo o código do aplicativo da Web é apresentado em Figura 3.

Do aplicativo da Web, a Figura 3

    <html>
    <head>
    <title>Item Color Web Application</title>
    <script language="JavaScript">
      function processclick() {
        if (document.all['TextBox1'].value == "") {
          alert("You must enter an item into the first box!");
        }
        else {
          var txt = document.all['TextBox1'].value;
          if (txt == "roses")
            document.all["TextBox2"].value = "red";
          else if (txt == "sky")
            document.all["TextBox2"].value = "blue";
          else
            document.all["TextBox2"].value = "I don't know that item";
        }
      }
    
    </script>
    </head>
    <body bgcolor="#F5DEB3">
      <h3>Color Identifier Web Application</h3>
      <p>Enter an item: 
        <input type="text" id="TextBox1" /></p>
      <p><input type="button" value="Click Me" 
                id="Button1" 
                onclick="processclick()"/></p>
      <p>Item color is: 
        <input type="text" id="TextBox2" /></p>
    </body>
    </html>

Eu salvei meu aplicativo Web como default. HTML em um diretório chamado ColorApp localizado no diretório C:\Inetpub\Wwwroot na minha máquina de host de teste. Para orientar limpar das questões de segurança, a técnica que apresento aqui funciona melhor quando a automação de teste é executado diretamente na máquina que atua como o servidor da Web que hospeda o aplicativo em teste. Para manter meu aplicativo Web de exemplo simples e não oculte detalhes sobre a automação de teste, fizemos atalhos que você não vê em um aplicativo da Web de produção, como, por exemplo, eliminando as verificações de erro.

O coração da funcionalidade do aplicativo Web está contido em uma função JavaScript chamada processclick. Essa função é chamada quando um usuário clica no controle de botão do aplicativo com ID de Button1 e o valor de rótulo “ Click me. ” A função processclick verifica primeiro se o valor em um elemento input TextBox1 está vazio. Em caso afirmativo, ele gera uma caixa de mensagem de erro usando a função de alerta do JavaScript. Se o elemento input TextBox1 não estiver vazio, a função processclick usa uma instrução if then para produzir um valor para o elemento TextBox2. Observe que, porque a funcionalidade do aplicativo da Web é fornecida por JavaScript do lado do cliente, o aplicativo não executará vários ciclos de cliente-servidor e, portanto, o aplicativo da Web é carregado apenas uma vez por funcionalidade ciclo.

A estrutura de teste do Windows Forms

Agora let’s percorrer o código de estrutura de teste ilustrado no do Figura 2, de modo que você poderá modificar o código para atender às suas necessidades. A estrutura de teste é um aplicativo do Windows FormsNormalmente eu usaria o Visual Studio para criar o programa. No entanto, eu vou mostrar como criar a estrutura usando o bloco de notas e o compilador c# de linha de comando, porque alguns conceitos importantes de ocultar a facilidade de uso e o código gerado automaticamente no Visual Studio. Assim que entender o meu código de exemplo, você não deve ter nenhum problema usando o Visual Studio em vez do bloco de notas.

Eu abrir o bloco de notas e começar a minha estrutura, declarando os espaços para nome usados:

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;

Você precisa de espaços para nome System, formulários e o desenho para a funcionalidade básica do Windows Forms. O namespace InteropServices permite que a estrutura de teste localizar e manipular as caixas de mensagem de janela restrita, usando o mecanismo P/Invoke. P/Invoke permite que você crie c# wrapper métodos que chamam funções nativas da API do Win32. O namespace de Threading é usado para a rotação de um thread separado que observa a aparência das caixas de mensagem.

Em seguida, declaro um espaço para nome do conjunto e começar ao código para a classe principal do aplicativo Windows Forms, que herda da classe System.Windows.Forms.Form:

namespace TestHarness
{
  public class Form1 : Form
  {
    [DllImport("user32.dll", 
      EntryPoint="FindWindow",
      CharSet=CharSet.Auto)]
    static extern IntPtr FindWindow(
      string lpClassName,
      string lpWindowName);
.
.
.

Imediatamente dentro da definição de Form1 coloque um atributo de escopo de classe que permite que a estrutura de teste chamar a função FindWindow do API externa, que está localizada na User32. dll. A função FindWindow da API é mapeada para um método c# também chamado de FindWindow, que aceita o nome interno de um controle de janela e retorna um identificador de IntPtr para o controle. O método FindWindow será usado pelo equipamento de teste para obter um identificador para a caixa de mensagem gerada pelo aplicativo da Web em teste.

Em seguida, adiciono duas mais atributos para habilitar a funcionalidade adicional da API do Win32:

[DllImport("user32.dll", EntryPoint="FindWindowEx",
  CharSet=CharSet.Auto)]
static extern IntPtr FindWindowEx(IntPtr hwndParent,
  IntPtr hwndChildAfter, string lpszClass, 
  string lpszWindow);

[DllImport("user32.dll", EntryPoint="PostMessage",
  CharSet=CharSet.Auto)]
static extern bool PostMessage1(IntPtr hWnd, uint Msg,
  int wParam, int lParam);

O método c# FindWindowEx associado com a função FindWindowEx da API será usado para obter um controle filho do controle localizado por FindWindow, isto é, o botão OK na caixa de mensagem. O método c# PostMessage1 associado com a função da API PostMessage serem usado para enviar um mouse - up e de mouse - mensagem — em outras palavras, um clique — para o botão OK.

Em seguida, declaro utilizem os três controles de escopo de classe que fazem parte do Windows Forms:

private WebBrowser wb = null;
private Button button1 = null;
private ListBox listBox1 = null;

O controle WebBrowser é um wrapper de código gerenciado para código nativo que abriga a funcionalidade do navegador Internet Explorer. O controle WebBrowser expõe métodos e propriedades que podem ser usadas para examinar e manipular uma página da Web hospedada no controle. O controle Button será usado para iniciar a automação de teste e o controle ListBox será usado para exibir mensagens de log de equipamento de teste.

Em seguida, eu começo o código para o construtor de Form1:

public Form1() {
    // button1
    button1 = new Button();
    button1.Location = 
      new Point(20, 430);
    button1.Size = new Size(90, 23);
    button1.Text = "Load and Test";
    button1.Click += 
      new EventHandler(
      this.button1_Click);
  .
.
.

Este código deve ser bastante auto-explicativo. É bem possível que você nunca teve escrever o código de interface do usuário do Windows Forms como esta do zero antes porque o Visual Studio faz como um bom trabalho de geração de código clichê de interface do usuário. Observe o padrão: instanciar um controle, defina as propriedades e, em seguida, conectar métodos manipuladores de eventos. Você verá esse mesmo padrão com o controle WebBrowser. Ao usar a técnica de automação de teste apresentadas neste artigo, é útil ter uma maneira de exibir o log mensagens e um controle ListBox funciona bem:

// listBox1
listBox1 = new ListBox();
listBox1.Location = new Point(10, 460);
listBox1.Size = new Size(460, 200);

Em seguida que configurar o controle WebBrowser:

// wb
wb = new System.Windows.Forms.WebBrowser();
wb.Location = new Point(10,10);
wb.Size = new Size(460, 400);
wb.DocumentCompleted +=
  new WebBrowserDocumentCompletedEventHandler(ExerciseApp);

A chave Observação aqui é que eu conectar um método de manipulador de eventos ao evento DocumentCompleted de modo que, após o aplicativo Web sob teste totalmente carregado no controle WebBrowser, controle de execução será transferido para um método definido pelo programa denominado ExerciseApp (que ainda não tiver ainda codifiquei). Isso é importante porque quase todas as situações haverá um atraso enquanto o aplicativo da Web está carregando e qualquer tentativa de acessar o aplicativo da Web no controle para que ele seja totalmente carregado lançará uma exceção.

Você deve ter adivinhado que é uma maneira de lidar com isso colocar uma instrução thread. Sleep para a estrutura de teste. Mas porque a estrutura e o controle WebBrowser estiver executando no mesmo segmento de execução, a instrução de dormir interromperá a estrutura de teste e o carregamento do WebBrowser.

Concluo o código do construtor de Form1, anexando as controles de usuário para o objeto de Form:

// Form1
  this.Text = "Lightweight Web Application Windows Forms Test Harness";
  this.Size = new Size(500, 710);
  this.Controls.Add(wb);
  this.Controls.Add(button1);
  this.Controls.Add(listBox1);
} // Form1()

Em seguida, eu código o método de manipulador de eventos para o controle de botão no equipamento de Windows Forms que ativa a automação de teste:

private void button1_Click(object sender, EventArgs e) {
  listBox1.Items.Add(
    "Loading Web app under test into WebBrowser control");
  wb.Url = new Uri(
    "http://localhost/ColorApp/default.html");
}

Depois de uma mensagem de log, instruem o controle WebBrowser para carregar o aplicativo Web sob teste, definindo a propriedade do controle Url. Observe que eu já codificados da URL do aplicativo em teste. A técnica que apresento aqui é mais adequada para a automação de teste leve, descartável, onde os valores dos parâmetros codificados tem menos desvantagens que em situações em que a automação de teste deve ser usada durante um longo período de tempo.

Em seguida, eu começo o código para o método ExerciseApp, que aceita controle de execução quando o evento DocumentCompleted é acionado:

private void ExerciseApp(object sender, EventArgs e) {
  Thread thread = new Thread(new
    ThreadStart(WatchForAndClickAwayMessageBox));
  thread.Start();

O método ExerciseApp contém a maior parte da lógica de equipamento de teste real. Começo a geração de um novo thread associado a um método definido pelo programa denominado WatchForAndClickAwayMessageBox. A idéia aqui é que quando uma caixa de mensagem de janela restrita é gerada pelo aplicativo da Web sob teste no controle WebBrowser, execução de equipamento de teste todos os será interrompido até que a caixa de mensagem é distribuídas virada com, o que significa que a estrutura de teste diretamente não é possível lidar com a caixa de mensagem. Assim, girando um thread separado que observa uma caixa de mensagem, a estrutura indiretamente pode lidar com a caixa de mensagem.

Em seguida, eu registrar uma mensagem e, em seguida, simular um usuário clicar no clique aplicativo da Web no me botão:

listBox1.Items.Add(
  "Clicking on 'Click Me' button");
HtmlElement btn1 = 
  wb.Document.GetElementById("button1");
btn1.InvokeMember("click");

O método GetElementById aceita a identificação de um elemento HTML que faz parte do documento carregado no controle WebBrowser. O método InvokeMember pode ser usado para disparar eventos, como clicar e mouseover. Porque não há nenhum texto no controle de TextBox1 do aplicativo da Web, o aplicativo Web irá gerar a caixa de mensagem de erro, que irá ser abordada pelo equipamento de método WatchForAndClickAwayMessageBox, como explicarei daqui a pouco.

Agora, supondo que a caixa de mensagem tem sido lidada com, posso continuar o cenário de teste:

listBox1.Items.Add("Waiting to click away message box");
listBox1.Items.Add("'Typing' roses into TextBox1");
HtmlElement tb1 = wb.Document.GetElementById("TextBox1");
tb1.InnerText = "roses";

Eu uso a propriedade InnerText para simular um usuário digitar “ rosas ” no controle TextBox1. Outras propriedades úteis para manipular o aplicativo Web sob teste são OuterText InnerHtml e OuterHtml.

Minha automação continua, simulando um clique do usuário no aplicativo Web do Click Me botão:

listBox1.Items.Add(
  "Clicking on 'Click Me' button again");
btn1 = wb.Document.GetElementById("button1");
btn1.InvokeMember("click");

Unlike the previous simulated click, this time there’s text in the TextBox1 control, so the Web application’s logic will display some result text in the TextBox2 control, and the test harness can check for an expected result and log a pass or fail message:

listBox1.Items.Add("Looking for 'red' in TextBox2");
HtmlElement tb2 = wb.Document.GetElementById("TextBox2");
string response = tb2.OuterHtml;
if (response.IndexOf("red") >= 0) {
  listBox1.Items.Add("Found 'red' in TextBox2");
  listBox1.Items.Add("Test scenario result: PASS");
}
else {
  listBox1.Items.Add("Did NOT find 'red' in TextBox2");
  listBox1.Items.Add("Test scenario result: **FAIL**");
}

Observe que a resposta HTML parecerá com < input type = valor ” texto ” = ” red ” / >, portanto, eu uso o método IndexOf para pesquisar o conteúdo de OuterHtml para o resultado esperado correto.

Esta é a definição para o método que tratam da caixa de mensagem modal do aplicativo Web:

private void WatchForAndClickAwayMessageBox() {
  IntPtr hMessBox = IntPtr.Zero;
  bool mbFound = false;
  int attempts = 0;
  string caption = "Message from webpage";
.
.
.

Eu declaro um identificador para a caixa de mensagem, uma variável Boolean para informar-me quando a caixa de mensagem foi encontrada, uma variável de contador, de forma que pode limitar o número de vezes que minha estrutura será a aparência da caixa de mensagem impedir que um loop infinito e a legenda da caixa de mensagem para procurar. Embora nesse caso, a legenda da caixa de mensagem é bastante óbvia, você sempre pode usar a ferramenta Spy + + para verificar se a propriedade caption de qualquer controle de janela.

Em seguida, eu código um loop assistindo:

do {
  hMessBox = FindWindow(null, caption);
  if (hMessBox == IntPtr.Zero) {
    listBox1.Items.Add("Watching for message box .
.
. "
);
    System.Threading.Thread.Sleep(100);
    ++attempts;
  }
  else {
    listBox1.Items.Add("Message box has been found");
    mbFound = true;
  }
} while (!mbFound && attempts < 250);

Eu uso um, faça - o ao loop repetidamente a tentativa de obter um identificador para uma caixa de mensagem. Se o retorno do FindWindow IntPtr. zero, eu 0,1 segundos de atraso e incrementar o contador da minha tentativa de loop. Se o retorno não for um IntPtr. zero, eu sei que eu tenha obtido um identificador para a caixa de mensagem e sair do - loop while. A “ tentativas <condição de 250 ” limitará a quantidade de tempo que meu equipamento está esperando por uma caixa de mensagem seja exibida. Dependendo da natureza de seu aplicativo da Web, convém modificar o tempo de espera e o número máximo de tentativas.

Depois do - while sai do loop, concluo o método WatchForAndClickAwayMessageBox, conferindo se a saída ocorreu porque uma caixa de mensagem foi encontrada ou porque a estrutura de tempo limite:

if (!mbFound) {
  listBox1.Items.Add("Did not find message box");
  listBox1.Items.Add("Test scenario result: **FAIL**");
}
else { 
  IntPtr hOkBtn = FindWindowEx(hMessBox, IntPtr.Zero, null, "OK");
  ClickOn(hOkBtn );
}

Se a caixa de mensagem não foi encontrada, eu classificar isso como uma falha no caso de teste e registrar esse resultado. Se a caixa de mensagem foi encontrada, uso o método FindWindowEx para obter uma alça de controle de filho de botão OK no controle de caixa de mensagem do pai e, em seguida, chamar um método definido pelo programa auxiliar denominado ClickOn, o que eu defino como:

private void ClickOn(IntPtr hControl) {
  uint WM_LBUTTONDOWN = 0x0201;
  uint WM_LBUTTONUP = 0x0202;
  PostMessage1(hControl, WM_LBUTTONDOWN, 0, 0);
  PostMessage1(hControl, WM_LBUTTONUP, 0, 0);
}

O Windows 0201h de constantes de mensagens e 0202h representam esquerdo do mouse-botão inferior e esquerda-mouse-botão-up, respectivamente. Eu uso o método de PostMessage1 é forçado para a função de API PostMessage do Win32, que descrevi anteriormente.

Meu equipamento de teste termina, definindo a estrutura de ponto de entrada do método principal:

[STAThread]
private static void Main() {
  Application.Run(new Form1());
}

Após salvar o conjunto de teste como Harness.cs, usei o compilador de linha de comando. Eu iniciar o shell de comando especial do Visual Studio (que sabe onde está o compilador c# exe), navegue até o diretório que contém meu arquivo Harness.cs e execute o comando:

C:\LocationOfHarness> csc.exe /t:winexe Harness.cs

O argumento /t:winexe instrui o compilador gere um executável em vez de executável de aplicativo de console padrão do Windows Forms. O resultado é um arquivo chamado Harness.exe, que pode ser executado a partir da linha de comando. Como mencionei anteriormente, você provavelmente desejará usar Visual Studio em vez do bloco de notas para criar a automação de teste com base no controle WebBrowser.

Conclusão

O exemplo que apresentei aqui deve fornecer informações suficientes para começar escrevendo sua próprias WebBrower de automação de teste com base no controle. Essa técnica é mais adequada para cenários de automação leve — situações em que você deseja obter sua automação de teste em funcionamento rapidamente e em que a automação tem um curto tempo de vida esperado. O nível de segurança dessa técnica é a capacidade de lidar com as caixas de mensagem modal — algo que pode ser bastante complicado, ao usar outras abordagens de automação de teste da interface do usuário. Essa técnica é especialmente útil quando você está testando a funcionalidade do aplicativo da Web que é gerada principalmente por JavaScript do lado do cliente.

Em situações onde a funcionalidade do aplicativo é gerada por vários cliente-servidor de Web arredondar viagens, você terá que modificar o código que apresentei porque sempre que uma resposta é retornada do servidor Web, o evento DocumentCompleted será disparado. Uma abordagem para lidar com isso é criar e usar uma variável que acompanha o número de eventos DocumentCompleted e adiciona sua estrutura lógica de ramificação.

Dr.Da James McCaffreytrabalha para a Volt Information Sciences Inc., onde gerencia o treinamento técnico para engenheiros de software trabalham em Redmond da Microsoft, Wash., campus. Para fazer trabalhou em vários produtos da Microsoft, incluindo o Internet Explorer e o MSN Busca.Dr.McCaffrey é autor de “ .NET Test Automation Recipes ” (Apress, 2006) e pode ser contatado pelo jammc@microsoft.comde .

Graças aos seguintes especialistas técnicos da Microsoft para revisão deste artigo: Paul Newson e do Dan Liebling