Il presente articolo è stato tradotto automaticamente.

Esecuzione di test

Test del servizio WCF con socket

Dr. James McCaffrey

Nell'articolo di mese questo sono unito da Carlos Figueira, un senior software engineer sviluppo test nel team Windows Communication Foundation (WCF). Con il suo aiuto ha intenzione di mostrare come verificare i servizi WCF utilizzando un approccio basato sui socket di rete.

Un buon metodo per visualizzare in cui sto angolare consiste nell'esaminare le schermate in Nelle figure 1, 2 e 3. Nella figura 1 viene illustrata un'applicazione Windows Form che ospita un servizio WCF semplice ma rappresentativo denominato MathService. Dietro le quinte MathService contiene un'unica operazione denominata somma che accetta due valori di tipo double, quindi calcola e restituisce la somma.

image: WCF MathService Service Under Test

Figura 1 Servizio WCF MathService Under Test

Nella figura 2 viene illustrato un tipico client dell'applicazione WCF ASP.NET Web che accetta due valori da parte dell'utente, invia al servizio MathService questi due valori, recupera la risposta dal servizio e viene visualizzato il risultato in un controllo ListBox.

image: Typical WCF ASP.NET Client

Nella figura 2 Client di ASP.NET WCF tipiche

Nella figura 3 viene illustrato un test harness applicazione console che esegue la verifica del servizio MathService funzionale. Il test harness invia i messaggi SOAP direttamente a un socket di rete utilizzando MathService accetta la risposta dal servizio e confronta un risultato previsto con il risultato effettivo per determinare un passaggio o esito negativo. Nel caso di prova 001 #, l'invia test harness è 3.4 e 5.7 al servizio WCF in test e riceve il valore previsto 9.1. Test case 002 # è un errore intenzionale, spuri solo a scopo dimostrativo.

Nelle sezioni che seguono, primi descrivere brevemente il servizio WCF da testare illustrato in Nella figura 1 in modo da necessario comprendere i fattori sono importanti durante la costruzione di automazione di test basata su socket WCF. Successivamente è possibile illustrare brevemente il client Web dimostrazione per fornire alcune informazioni sulle prove basate su socket è più appropriata rispetto a tecniche alternative. Quindi è possibile illustrare in dettaglio il codice che ha creato il test harness in modo che possano essere in grado di adattare la tecnica qui presentate in base alle proprie esigenze. In questo articolo si presuppone livello intermedio C# le competenze di codifica.

image: WCF Test Harness Run

Nella figura 3 Esegui test harness WCF

Il servizio WCF Under Test

Ho utilizzato Visual Studio 2008 per creare il servizio WCF da testare. Una delle funzionalità preciso sui servizi di WCF è che possono essere ospitati in molti tipi diversi di applicazioni. Ho deciso di host di WCF MathService del servizio in un'applicazione Windows Form, ma le tecniche presentate in questo articolo di lavoro con qualsiasi tipo di host WCF.

Dopo aver creato un Windows Form vuoto, aggiunto due controlli Button e un controllo ListBox. Quindi, sotto la definizione della maschera, ho aggiunto il semplice codice necessario per dichiarare un servizio WCF:

[ServiceContract]
   public interface IMathService {
   [OperationContract]
     double Sum(double x, double y);
   }

L'attributo ServiceContract applicato a un'interfaccia genera tutto il codice necessario per un'interfaccia WCF. Se si desidera spostare a WCF da servizi Web, è possibile pensare a un attributo OperationContract come analoga all'attributo WebMethod. Implementazione del servizio WCF è semplice:

public class MathService : IMathService  {
  public double Sum(double x, double y) {
    double answer = x + y;
    return answer;
  }
}

Con il plumbing WCF posto, ho aggiunto un riferimento a System.ServiceModel.dll assembly .NET che contiene funzionalità WCF, al progetto. Quindi ho aggiunto utilizzando le istruzioni agli spazi dei due chiavi .NET nomi contenuti nell'assembly necessari per il servizio WCF:

using System.ServiceModel;
using System.ServiceModel.Description;

Lo spazio dei nomi ServiceModel contiene la classe ServiceHost e diverse classi che definiscono le associazioni WCF. Lo spazio dei nomi Descrizione contiene la classe ServiceMetadataBehavior che consente di pubblicare informazioni per il servizio WCF. Quindi ho aggiunto logica la creazione di istanze del servizio al gestore eventi del controllo Button1:

try {
  string address = 
    "http://localhost:8000/MyWCFMathService/Service";
  Uri baseAddress = new Uri(address);
  serviceHost = 
    new ServiceHost(typeof(MathService), baseAddress);
  serviceHost.AddServiceEndpoint(typeof(IMathService),
    new WSHttpBinding(SecurityMode.None), "MathService");
  . . .

Fattore fondamentale da notare è che è possibile creare un endpoint WCF utilizzando WSHttpBinding. Le associazioni WCF sono un insieme che include informazioni su secutity e impostazioni reliabilty, protocollo di trasporto e tipo di codifica. Il WSHttpBinding è un'associazione di interesse generale eccellente per la comunicazione duplex non. Per impostazione predefinita, WSHttpBinding utilizza trasmissione crittografata, ma qui è possibile specificare SecurityMode.None in modo che è possibile visualizzare più facilmente i dati di tipo richiesta-risposta.

Successivamente si aggiunge codice per rendere visibile ai client del servizio tramite il meccanismo di Add Service Reference:

ServiceMetadataBehavior smb = 
  new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
serviceHost.Description.Behaviors.Add(smb);
. . .

A questo punto sono pronto per aggiungere il codice che avvia un servizio WCF:

serviceHost.Open();
int count = 0;
while (serviceHost.State != 
       CommunicationState.Opened && 
       count < 50) {
  System.Threading.Thread.Sleep(100);
  ++count;
}
. . .

Utilizzare un metodo semplice ma efficacia per rilevare il blocco del servizio all'avvio tramite un ciclo di ritardo di 0,1 secondi al ritardo fino a un massimo di 50 ritardi. Una volta terminato il ciclo di ritardo, il servizio WCF è stato avviato o è stato raggiunto il numero massimo di ritardi:

if (serviceHost.State == CommunicationState.Opened)
  listBox1.Items.Add("WCF MathService is started");
else
  throw new Exception(
    "Unable to start WCF MathService in a timely manner");

Sto prendendo molti tasti di scelta rapida che non sarebbe assumere durante la scrittura di codice di produzione, ad esempio il controllo non visualizzare se l'oggetto serviceHost è già aperto prima di tentare di aprirlo. La logica nel gestore di eventi controllo Button2 chiude MathService WCF e utilizzare tale modello stesso per avviare il servizio:

int count = 0;
serviceHost.Close();
while (serviceHost.State != 
       CommunicationState.Closed && 
       count < 50) {
  System.Threading.Thread.Sleep(100);
  ++count;
}

Un client di applicazione Web tipica

Ho utilizzato Visual Studio 2008 per creare client dell'applicazione WCF Web illustrato in Nella figura 2. Iniziare facendo clic su file | nuovo | sito Web. Nel nuova sito Web della finestra di dialogo, è destinato Microsoft .NET Framework 3.5 modello Empty Web Site selezionato, scelto un percorso di File System utilizzando il linguaggio C# e denominato mio progetto WCFMathServiceWebClient.

Successivamente, in Esplora soluzioni, fa clic con il pulsante destro del mouse sul progetto e selezionata l'opzione Aggiungi nuovo elemento dal menu di scelta rapida. Nella finestra di dialogo risultante selezionato l'elemento Web Form. L'opzione “ Inserisci codice in file separato ” è deselezionato, in modo potuto inserire tutto il mio codice dell'applicazione in un unico file.

Dopo aver aggiunto al Web Form, ho aggiunto i tag del controllo lato server per creare l'interfaccia utente molto semplice dell'applicazione Web:

<asp:Label ID="Label1" runat="server" 
  Text="Enter first number: "/>
<asp:TextBox ID="TextBox1" runat="server" /><p />
<asp:Label ID="Label2" runat="server" 
  Text="Enter second number: "/>
<asp:TextBox ID="TextBox2" runat="server" /><p />
<asp:Button ID="Button1" runat="server" Text="Compute Sum" 
  onclick="Button1_Click" /><p />
<asp:ListBox ID="ListBox1" runat="server" />

Successivamente, premere il tasto F5 per indicare a Visual Studio per generare l'applicazione e Chiedi conferma per consentire la generazione automatica di un file Web.config per il progetto. Dopo la creazione del file Web.config OK’d I, ho iniziato il servizio WCF MathService, il mio progetto client sarà in grado di visualizzare il servizio.

È stato fatto clic con il pulsante destro del mouse sul progetto in Esplora soluzioni client ed è selezionata l'opzione Add Service Reference dal menu di scelta rapida. Nella finestra di dialogo Add Service Reference dopo avere immesso il percorso del mio servizio WCF (http://localhost:8000/ MyWCFMathService / Service) e si fa clic sul pulsante Vai. Poiché è in esecuzione il servizio WCF e il servizio pubblica metadati su se stesso, lo strumento di Visual Studio troverà il servizio. È possibile rinominare lo spazio dei nomi servizio da predefinito ServiceReference1 in WCFMathServiceReference.

Dietro le quinte, Visual Studio aggiunge informazioni relative al servizio WCF al file Web.config mediante la creazione di una voce <system.serviceModel>. Che voce contiene un elemento <endpoint>con l'attributo associazione = ” wsHttpBinding ” in modo che il client dell'applicazione Web è in grado di comunicare con il servizio WCF. In visualizzazione Progettazione, fatto doppio clic sul controllo Button1 per registrare il gestore eventi e aggiungere logica di accesso al servizio:

try {
  WCFMathServiceReference.MathServiceClient sc =
    new WCFMathServiceReference.MathServiceClient();
  double x = double.Parse(TextBox1.Text);
  double y = double.Parse(TextBox2.Text);
  double sum = sc.Sum(x, y);
  . . . 
  ListBox1.Items.Add(
    "The response from the WCF service is " + sum);

Il nome della classe che contiene l'operazione SUM è MathServiceClient, ovvero in altre parole, il nome della classe (MathService) derivata dall'interfaccia contratto WCF (IMathService) con “ client ” accodato.

Test harness

Visual Studio 2008 inoltre utilizzato per creare un progetto C# console applicazione denominato WCFTestHarness. Nella parte superiore del file Program.cs generato verrà aggiunto queste istruzioni using:

using System;
using System.Text; 
using System.Net; 
using System.Net.Sockets;

È necessario che le classi nello spazio dei nomi System.text per convertire testo in matrici di byte perché TCP funziona a livello di byte. È necessario che le classi nello spazio dei nomi System.NET per creare oggetti che sono astrazioni di indirizzi IP. Ed è necessario lo spazio dei nomi System.NET.Sockets per creare un oggetto socket che esegue l'invio effettivo e operazioni di ricezione. Ho aggiunto un messaggio di registrazione breve per il metodo Main e quindi impostare i dati del test case sotto forma di matrice di stringhe:

namespace WCFTestHarness {
  class Program {
    static void Main(string[] args) {
      try {
        Console.WriteLine(
        "\nBegin WCF MathService testing via sockets run");

        string[] testCases = new string[] {
          "001,3.4,5.7,9.1",
          "002,0.0,0.1,1.0",
          "003,6.7,6.7,13.4"
        };
        . . .

Ogni stringa di dati di test case contiene quattro campi delimitati da virgole: ID di test case, ovvero due input per l'operazione SUM e un valore di risposta prevista. È possibile utilizzare i dati del test case per creare il ciclo principale test harness elaborazione:

foreach (string testCase in testCases) {
  Console.WriteLine("\n============\n");
  string[] tokens = testCase.Split(',');
  string caseID = tokens[0];
  string input1 = tokens[1];
  string input2 = tokens[2];
  string expected = tokens[3];
  . . .

Viene utilizzato il metodo Split violare i quattro campi di ogni stringa.

Segue una delle parti chiave dell'invio di input per un servizio WCF tramite socket. Creazione di un messaggio SOAP come illustrato in Nella figura 4.

Nella figura 4 Messaggio SOAP per il test del servizio WCF

string soapMessage = "<s:Envelope xmlns:s='http://www.w3.org/2003/05/soap-envelope'";
soapMessage += " xmlns:a='http://www.w3.org/2005/08/addressing'><s:Header>";
soapMessage += "<a:Action s:mustUnderstand='1'>http://tempuri.org/IMathService/Sum</a:Action>";
soapMessage += "<a:MessageID>urn:uuid:510b1790-0b89-4c85-8015-d1043ffeea14</a:MessageID>";
soapMessage += "<a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>";
soapMessage += "</a:ReplyTo><a:To s:mustUnderstand='1'>";
soapMessage += "http://localhost:8000/MyWCFMathService/Service/MathService</a:To></s:Header>";
soapMessage += "<s:Body><Sum xmlns='http://tempuri.org/'>";

soapMessage += "<x>" + input1 + "</x>" + "<y>" + input2 + "</y>";

soapMessage += "</Sum></s:Body></s:Envelope>";

Si noti che i valori di input di test case, input1 e input2, vengono incorporati in messaggio SOAP all'interno di un elemento <sum>verso la fine del messaggio. Ma come è stato determina la struttura di messaggio non in modo semplice?

Esistono diversi modi è possibile determinare la struttura di messaggio di richiesta SOAP e i dati per un servizio WCF. L'approccio più semplice e quello che ho utilizzato consiste nell'utilizzare uno strumento di esame del traffico di rete, ad esempio netmon o Fiddler per acquisire i dati durante l'esercizio di un'applicazione client. In altre parole, con il servizio WCF in esecuzione e in esecuzione anche uno strumento di cattura il traffico (utilizzato Fiddler), avviato il programma client di applicazione Web illustrato in Nella figura 2 e utilizzato il client per inviare una richiesta al servizio WCF. Lo strumento di cattura il traffico di rete ha dimostrato invia il messaggio SOAP inviato dal client al servizio WCF. Ho utilizzato le informazioni per creare il messaggio SOAP nel test harness.

Con il messaggio SOAP costruito, successivamente è visualizzato il test case di input come i dati previsti la shell:

Console.WriteLine(
  "Test Case   : " + caseID);
Console.WriteLine(
  "Input       : <s:Envelope..." + "<x>" +
  input1 + "</x>" + "<y>" + input2 + 
  "</y>...</s:Envelope>");
Console.WriteLine("Expected    : " + expected);

Quindi si imposta l'indirizzo IP della destinazione del servizio WCF MathService testare:

string host = "localhost";
   IPHostEntry iphe = Dns.Resolve(host);
   IPAddress[] addList = iphe.AddressList;
   EndPoint ep = new IPEndPoint(addList[0], 8000);

Il metodo Dns.Resolve restituisce un elenco di indirizzi IP (potrebbero esservi più di uno) associato a un nome di computer host particolare come oggetto IPHostEntry. L'oggetto IPHostEntry dispone di una proprietà AddressList è una matrice di oggetti IndirizzoIP. Un oggetto EndPoint è costituito da un oggetto IndirizzoIP oltre a un numero di porta. A questo punto posso creare il socket:

Socket socket = new Socket(AddressFamily.InterNetwork,
  SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ep);
if (socket.Connected)
  Console.WriteLine(
    "Connected to socket at " + ep.ToString());
else
  Console.WriteLine(
    "Error: Unable to connect to " + ep.ToString());

Gli oggetti socket implementare l'interfaccia socket Berkeley, che è un meccanismo di astrazione per l'invio e la ricezione del traffico di rete. Il primo argomento al costruttore socket specifica lo schema di indirizzamento. Qui utilizzerò InterNetwork, è normale IPv4.

Il secondo argomento al costruttore socket specifica quali sei tipi possibili socket da creare. Flusso indica un socket TCP. L'altro tipo comune è Dgram, viene utilizzato per i socket UDP (User Datagram Protocol).

Il terzo argomento al costruttore socket specifica i protocolli supportati dal socket. Il metodo Socket.Connect accetta un oggetto EndPoint.

Successivamente è possibile costruire le informazioni di intestazione:

string header = 
  "POST /MyWCFMathService/Service/MathService HTTP/1.1\r\n";
header += "Content-Type: application/soap+xml; charset=utf-8\r\n";
header += "SOAPAction: 'http://tempuri.org/IMathService/Sum'\r\n";
header += "Host: localhost:8000\r\n";
header += "Content-Length: " + soapMessage.Length + "\r\n";
header += "Expect: 100-continue\r\n";
//header += "Connection: Keep-Alive\r\n\r\n";
header += "Connection: Close\r\n\r\n";

Proprio come con il messaggio SOAP è determinata le informazioni dell'intestazione utilizzando un strumento di monitoraggio del traffico di rete. Si noti che ogni riga termina con un carattere di terminazione \r\n (ritorno a capo, avanzamento riga) anziché un token singolo \n (nuova riga) e l'ultima riga termina con una doppia \r\n. Come è stato indicato dal riga con commento di codice precedente, in base a diversi fattori potrebbe essere necessario inviare un keep-alive o un argomento di chiusura per la voce di intestazione Connection. Analogamente, non è necessario un WSHttpBinding intestazione SOAPAction.

Ora posso combinare intestazione e il messaggio SOAP, convertire l'intero messaggio in byte e inviare la richiesta:

string sendAsString = header + soapMessage;
byte[] sendAsBytes = Encoding.UTF8.GetBytes(sendAsString);
Console.WriteLine("Sending input to WCF service");
int numBytesSent = socket.Send(sendAsBytes, sendAsBytes.Length,
  SocketFlags.None);

Il metodo Socket.Send ha diversi overload. Qui è possibile inviare l'intera richiesta senza alcuna speciale inviare o ricevere le opzioni. Non rendere utilizzare del valore restituito, ma ho potuto hanno utilizzato tale valore per verificare che sia stato inviato l'intero messaggio. A questo punto è possibile recuperare la risposta dal servizio WCF:

byte[] receivedBufferAsBytes = new byte[512];
string receiveAsString = "";
string entireReceive = "";
int numBytesReceived = 0;

while ((numBytesReceived =
  socket.Receive(receivedBufferAsBytes, 512,
  SocketFlags.None)) > 0) {
  receiveAsString =
    Encoding.UTF8.GetString(receivedBufferAsBytes, 0,
    numBytesReceived);
  entireReceive += receiveAsString;
}

Poiché nella maggior parte dei casi non è possibile prevedere il numero di byte che saranno nella risposta, l'idea è creare un buffer e leggere blocchi di risposta finché l'intera risposta utilizzato. Di seguito viene utilizzato un buffer di dimensione 512. Come ogni gruppo di 512 byte vengono ricevuti, sono convertite in testo e aggiungere alla stringa di una risultato aggregato.

Con la risposta ricevuta, è necessario controllare la risposta per verificare se contiene il valore previsto test case corrente:

Console.WriteLine("Response received");
if (entireReceive.IndexOf(expected) >= 0)
  Console.WriteLine("Test result : Pass");
else
  Console.WriteLine("Test result : **FAIL**");

L'approccio che utilizzare in questo caso è efficacia per scenari di testing molto semplice, ma potrebbe essere necessario aggiungere logica aggiuntiva se lo scenario di testing è più complicato.È possibile terminare harness da impegnare loose estremità:

. . . 
        } // main loop
        Console.WriteLine(
          "\n=======================================");
        Console.WriteLine("\nEnd test run");
      } // try
      catch (Exception ex)
      {
        Console.WriteLine("Fatal: " + ex.Message);
      }
    } // Main()
  } // Program
} // ns

Conclusioni

Sono disponibili molte alternative al test WCF, ma l'approccio al test di servizi WCF basate su socket è estremamente flessibile. Poiché TCP e socket sono costrutti di basso livello, funzionano in una vasta gamma di scenari, in particolare quando si esegue il test in un ambiente eterogeneo technologically. Ad esempio, si potrebbe verificare un servizio WCF ospitato in una piattaforma Windows da un client non Windows. Anche se si desidera utilizzare modificare il codice C# specifico presentate in questo articolo per una lingua supportata dal client (in genere c ++ o Perl), la tecnica generale saranno gli stessi.

Inoltre, un approccio basato su socket è molto utile per test di protezione (si deve presupporre che un servizio WCF sarà soggetto alle probe non adatto al socket) e verifica delle prestazioni (un approccio di socket è diretto e può fornire i dati di previsione tempi di andata e ritorno).

Dr.James McCaffrey * lavora per Volt Information Sciences Inc., dove gestisce la formazione tecnica degli ingegneri software di lavoro in Microsoft Redmond, WA, campus. Si è occupato di numerosi prodotti Microsoft inclusi in Internet Explorer e MSN Search. Dr. McCaffrey è l'autore di “ .NET Test Automation Recipes ” (Apress, 2006) ed è possibile contattarlo all'indirizzo jammc@microsoft.com.*

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Carlos Figueira