Condividi tramite


Il presente articolo è stato tradotto automaticamente.

Esecuzione di test

Web Application richiesta-risposta test con JavaScript

Dr. James McCaffrey

Scaricare il codice di esempio.

Nell'articolo di mese questo illustrano come scrivere l'automazione di test semplice ed efficace basato su browser richiesta-risposta utilizzando JavaScript. È il modo migliore vedere dove sono a frecce per esaminare le schermate in le figure 1 e 2. Nella figura 1 viene illustrata un'applicazione ASP.NET Web semplice ma rappresentativa sottoposta a test denominato servizio Search. Un utente immette una stringa di ricerca nel controllo casella di testo singola dell'applicazione e specifica se la ricerca deve essere eseguita in modo rilevante la distinzione tra maiuscole e minuscole utilizzando due controlli pulsante di opzione. I risultati della ricerca vengono visualizzati in un controllo casella di riepilogo.

Figura 1 applicazione Web Product ricerca Under Test

image: Product Search Web Application Under Test

Esempio di applicazione Web sottoposta a test è basato su ASP.NET, la tecnica presentate in questo articolo può essere utilizzata per creare l'automazione di test per le applicazioni Web scritte utilizzando tecnologie di generazione pagina più dinamiche, tra cui PHP, JSP, CGI e ad altri utenti.

Nella figura 2 viene illustrata l'automazione di test richiesta-risposta in azione. Si noti che il test harness di automazione è basata sul browser. Uno dei vantaggi della tecnica che presenterò qui rispetto ad approcci alternativi è la tecnica può essere utilizzata con la maggior parte dei browser principali che può essere eseguita su computer di prova host che esegue la maggior parte dei sistemi operativi.

Nella figura 2 richiesta-risposta Test Run

image: Request-Response Test Run

Il test harness di automazione è costituito da una pagina HTML che ospita un set di funzioni JavaScript relativamente breve. Si noti che la prima riga di output di esecuzione dei test indica che l'automazione di test utilizza la libreria jQuery. L'harness legge dati di input test case che corrisponde all'input dell'utente, e a livello di programmazione post input dati all'applicazione Web di ricerca di clienti. L'harness accetta i dati di risposta HTTP risultanti ed esamina tale risposta per un valore previsto per determinare se superato o meno il test case.

Nelle sezioni di questo articolo che seguono, innanzitutto descrivere brevemente l'applicazione Web sottoposta a test illustrato in Figura 1 in modo che sia necessario comprendere i fattori sono importanti per test di richiesta / risposta HTTP. Successivamente è possibile spiegare in dettaglio il test di sfruttare il codice illustrato in esecuzione in Figura 2, sarà possibile modificare la combinazione per soddisfare esigenze specifiche. Concludere con alcuni commenti sulle quando basato su browser richiesta-risposta di automazione di test con JavaScript è appropriato e quando approcci alternativi potrebbero essere più adatti.

In questo articolo presuppone che si dispone di livello intermedio JavaScript e le competenze ASP.NET, ma anche se siete principianti con queste tecnologie dovrebbe essere in grado di seguire le spiegazioni senza eccessiva difficoltà.

Creazione dell'applicazione Web

Ho utilizzato Visual Studio 2008 per creare l'applicazione Web Cerca clienti sottoposta a test. Per sfruttare possibilità della Visual Studio di configurare un sito Web, selezionata l'opzione file | nuovo | sito Web dalla barra dei menu principale. Successivamente è stata selezionata l'opzione sito Web vuoto risultante nuovo sito Web nella finestra di dialogo. È stato specificato un percorso HTTP sul mio computer locale per creare un sito ASP.NET Web completato, anziché specificare un percorso del file System per l'utilizzo del server di sviluppo Visual Studio incorporato. Selezionato il linguaggio C# per il codice della logica.

Dopo aver scelto OK, verrà creato il sito ProductSearch Web vuoto. Nella finestra Esplora soluzioni si fa clic con il pulsante destro del mouse sul progetto ProductSearch e seleziona Aggiungi nuovo elemento dal menu di scelta rapida. Ho selezionato l'elemento Web Form e accettato il nome della pagina predefinita di default.aspx e si fa clic su Aggiungi per generare la pagina. Successivamente è stata creata l'interfaccia utente semplice per l'applicazione Web sottoposta a test, come viene presentato in Figura 3.

Nella figura 3 App interfaccia

<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>

Come verrà illustrato più avanti, durante la creazione di automazione di test richiesta / risposta HTTP è necessario conoscere gli ID di qualsiasi dell'input dei controlli che si desidera simulare l'input dell'utente. In questo caso si dispone dell'accesso di codice sorgente dell'applicazione sottoposta a test, ma anche se non si dispone di accesso di codice sorgente è sempre possibile determinare il controllo di input ID utilizzando la funzionalità di visualizzazione origine di un browser Web. Si noti che i controlli pulsante di due opzione sono rappresentati da un singolo controllo di input con ID RadioButtonList1 anziché da due controlli come può immaginare.

È stata aggiunta la logica dell'applicazione direttamente nel file Defaut.aspx anziché utilizzando il meccanismo di code-behind. Nella parte superiore della pagina creata per contenere il codice dell'applicazione logica di un blocco di script:

<%@ 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>

Ho aggiunto una piccola classe nel blocco di script per rappresentare un oggetto Product:

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

Quindi ho aggiunto un oggetto ArrayList ambito applicazione interno per simulare un archivio dati esterno:

public static ArrayList data = null;

In scenari di applicazioni Web più realistici, archivi dati sono in genere esterni, ad esempio un file XML o un database di SQL Server. Tuttavia, quando si esegue la risposta di richiesta HTTP test, la posizione dell'archivio dati di un'applicazione è irrilevante in qualche misura. La richiesta HTTP non è a conoscenza della posizione dell'archivio dati e la risposta HTTP contiene in genere solo HTML. Quindi ho aggiunto codice per popolare l'archivio dati interno con elementi Product:

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);
  }
}

Infine, ho inserito tutta la logica dell'applicazione nel gestore eventi per evento click di Button1. Inizio deselezionando l'area risultati ListBox1 e recupero input dell'utente:

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

La variabile di stringa sensibilità conterrà “ case sensibili ” o “ non case sensitive. ”

Quindi è possibile inserire le informazioni di intestazione nell'area risultato ListBox1 e dichiarare una stringa per contenere un risultato di ricerca di prodotti e inizializzare un contatore per tenere traccia di quanti elementi prodotto corrispondono al filtro di ricerca:

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

È possibile scorrere ogni oggetto Product nell'archivio dati ArrayList un controllo per verificare se la stringa di filtro di ricerca corrisponde al campo della descrizione dell'oggetto corrente:

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);
}

Per ogni prodotto che corrisponde al filtro di ricerca, si genera una stringa di risultati e incrementa il contatore visite. Si noti che il metodo IndexOf per comodità è in overload per accettare un argomento di distinzione tra maiuscole e minuscole.

La logica dell'applicazione al termine dell'aggiunta di una riga vuota e un riepilogo di conteggio a ListBox1 l'area di visualizzazione:

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

Per mantenere le dimensioni dell'applicazione Web come piccoli e più semplice possibile, ho preso in considerazione molti tasti di scelta rapida che non sarebbe utilizzare in un ambiente di produzione. In particolare non ho fornito qualsiasi errore di verifica o la gestione di questo tipo.

Automazione di test richiesta-risposta

Ho creato un test harness pagina visualizzata in esecuzione in Figura 2 utilizzando blocco note. Figura 4 è illustrata la struttura complessiva dell'harness.

Nella figura 4 struttura del test harness

<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>

Harness codice dell'interfaccia utente nell'elemento corpo nella parte inferiore della pagina è costituito solo da parte del testo, un elemento textarea per visualizzare informazioni e un pulsante per avviare l'automazione di test.

La struttura di test harness inizia utilizzando l'attributo src dell'elemento dello script per fare riferimento alla libreria jQuery. La libreria jQuery è un insieme di open source di funzioni JavaScript disponibili da jquery.com. Sebbene jQuery è stato creato con lo sviluppo Web presente, la libreria contiene funzioni che lo rendono adatto per l'automazione di test leggero richiesta-risposta. Qui è possibile posizionare una copia locale della versione 1.3.2 della libreria. Per motivi di automazione di test, utilizzando una copia locale della libreria è più che punta a una copia remota affidabile. Successivamente viene utilizzato il linguaggio di jQuery .ready $ (documento) per assicurarsi che l'harness può accedere alla raccolta e che l'harness DOM viene caricato in memoria.

Dopo aver impostato una variabile targetURL che punta all'applicazione Web sottoposta a test, è difficile il codice interni casi di prova separato da virgole in una matrice di stringhe denominata testCaseData. Qui ho solo due casi di prova, ma in un ambiente di produzione potrebbe essere centinaia di casi. I dati esterni test case sono spesso preferibili per i dati interni test case, in quanto dati esterni possono essere modificato in modo più semplice e condiviso. Tuttavia, poiché la tecnica che mi presentazione di questo campo è leggera, i dati interni test case sono una scelta ragionevole.

Il primo campo in un test case è un numero ID del caso. Il secondo campo è dati richiesta non elaborato da inviare all'applicazione sottoposta a test. Il terzo campo è un risultato previsto.

Come è stato conoscere il formato dei dati della richiesta? Il modo più semplice per determinare il formato dei dati di richiesta HTTP è eseguire esperimenti con l'applicazione sottoposta a test preliminare esaminando i dati effettiva richiesta utilizzando uno strumento di logger HTTP, ad esempio Fiddler.

Esecuzione dei test

La funzione di controllo harness principale è denominata runTests. La funzione runTests utilizza un meccanismo di primo livello try-catch per fornire la gestione degli errori rudimentali. È possibile utilizzare una funzione ausiliaria denominata logRemark per visualizzare le informazioni all'elemento textarea harness. L'harness getVS funzioni di supporto e getEV viene utilizzato per ottenere i valori correnti di ViewState e EventValidation dell'applicazione ASP.NET Web sottoposta a test. Questi valori generati dall'applicazione con codifica Base64 fungono principalmente da meccanismi di protezione e lo stato e devono essere inviati come parte di qualsiasi richiesta HTTP POST. La funzione sendAndReceive esegue la richiesta HTTP effettiva e restituisce la risposta HTTP corrispondente. La funzione runTests scorre ogni test case:

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];
  ...

Incorporato suddividere funzione utilizzare per separare ciascuna stringa test case in parti più piccole. Successivamente viene chiamato il getVS e getEV funzioni di supporto:

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

Il ciclo di elaborazione principale continua chiamando la funzione sendAndReceive ed esaminando la risposta HTTP risultante per il valore previsto associato test case:

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

La funzione di supporto getVS si basa sulla libreria 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);
}

L'idea principale della funzione getVS consiste nell'inviare una richiesta GET priming per l'applicazione da testare, recuperare la risposta e analizzare il valore di ViewState. La funzione .ajax $ accetta una funzione anonima. Async e tipo di parametri URL devono essere abbastanza facile interpretazione. Il metodo hasOwnProperty(“d”) dell'oggetto risposta risposta è essenzialmente un meccanismo di protezione presente in Microsoft .NET Framework 3.5 e non è necessario in questa situazione.

È possibile estrarre il valore di ViewState cercando l'inizio dell'attributo, quindi conteggio oltre 24 caratteri da cui inizia effettivamente il valore di ViewState. Il codice della funzione getEV corrisponde esattamente come il getVS codice ad eccezione del fatto che il valore EventValidation 30 caratteri viene avviata dall'id iniziale = attributo EVENTVALIDATION. GetVS separato e funzioni getEV offre flessibilità ma richiede due richieste priming separato. In alternativa, è possibile effettuare il refactoring getVS e getEV in una funzione di supporto unico.

La funzione di supporto sendAndReceive esegue la richiesta HTTP effettiva e recupera la risposta risultante. La funzione inizia con la conversione di ViewState non elaborati e EventValidation stringhe in stringhe con codifica URL e costruisce quindi i dati da inviare all'applicazione Web:

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

La funzione incorporata encodeURIComponent codifica i caratteri che non sono valori validi nei dati di post in una sequenza di escape. Ad esempio, il ‘ / ’ carattere è codificato come % 2F. Dopo un messaggio di registrazione sendAndReceive utilizzato il metodo .ajax $ per creare una richiesta HTTP POST:

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

Il metodo .ajax $ è stato creato principalmente per inviare richieste XML HTTP asincrone, ma impostando il parametro async su false il metodo può essere utilizzato per inviare le richieste sincrone standard. Utile, vero? Il valore del parametro di tipo di contenuto può essere considerato come una stringa Bacchetta significa semplicemente i dati registrati da un elemento HTML form. La funzione sendAndReceive utilizza lo stesso schema come getVS per ottenere la risposta HTTP associata:

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

Anche possibile utilizzare il parametro facoltativo errore per visualizzare eventuali errori irreversibili in una finestra di avviso.

La funzione finale nel test harness è l'utilità logRemark:

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

Utilizzo jQuery selettore e la sintassi di concatenamento per ottenere il testo corrente nell'elemento textarea, che dispone di un ID di commenti. Il ‘ # ’ sintassi viene utilizzato per selezionare un elemento HTML da ID e la funzione val può essere utilizzato come un getter e setter di valore. È possibile accodare il valore del parametro di commento e un carattere di nuova riga per il testo del commento esistente e quindi utilizzare la sintassi jQuery per aggiornare l'elemento textarea.

Soluzioni alternative

Alternativa principale per il browser basato su JavaScript lingua presentate in questo articolo consiste nel creare un harness basati su shell utilizzando un linguaggio come C#. Rispetto a un approccio basato su shell, l'approccio basato su browser risulta particolarmente utile quando si lavora in un ambiente estremamente dinamico in cui l'automazione di test ha una durata breve. Inoltre, l'approccio basato su browser qui presentata è abbastanza indipendente dalla piattaforma. La tecnica funzionerà con qualsiasi browser e combinazione OS che supporta la libreria jQuery e JavaScript.

Dr. James McCaffrey* lavora per Volt Information Sciences Inc., dove gestisce la formazione tecnica degli ingegneri software Microsoft. Si è occupato di numerosi prodotti Microsoft inclusi in Internet Explorer e MSN Search. Dr. McCaffrey è l'autore di*  .NET Test Automation Recipes(Apress, 2006) e può essere contattato all'indirizzo jmccaffrey@volt.com o v-jammc@microsoft.com.