Dieser Artikel wurde maschinell übersetzt.
Testlauf
Testen von WCF-Diensten mit Sockets
Dr. James McCaffrey
In der Kolumne dieses Monats werde ich durch Carlos Figueira, als senior Software Entwicklung Engineer in Test im Team Windows Communication Foundation (WCF) miteinander verknüpft. Mit seiner Hilfe möchten Ihnen zeigen, wie WCF-Dienste, die in einem Netzwerk socketbasierte Ansatz getestet werden.
Eine gute Möglichkeit, damit Sie sehen, wo ich weisenden bin besteht darin, untersuchen Sie die Abbildungen in Zahlen 1, 2 und 3. Abbildung 1 zeigt eine Windows Forms-Anwendung, die einen einfachen-aber-Vertreter WCF-Dienst namens MathService hostet. Hinter den Kulissen enthält MathService eine einzelne Operation mit der Bezeichnung Summe, die akzeptiert zwei Werte vom Typ double, dann berechnet, und gibt die Summe zurück.
Abbildung 1 WCF MathService-Dienst unter Test
Abbildung 2 zeigt einen typischen WCF ASP.NET Anwendung Webclient, der akzeptiert zwei Werte aus der Benutzer, diese beiden Werte an den MathService-Dienst gesendet, die Antwort vom Dienst abruft und zeigt das Ergebnis in einem ListBox-Steuerelement.
Abbildung 2 Typische WCF ASP.NET Client
Abbildung 3 zeigt eine Testumgebung Konsole Anwendung, die funktionale Überprüfung des Dienstes MathService durchführt. Die Testumgebung sendet SOAP-Nachrichten direkt an MathService über einen Netzwerksocket akzeptiert die Antwort vom Dienst und vergleicht ein erwartetes Ergebnis mit dem tatsächlichen Ergebnis bestimmen einen Durchlauf wird oder fehlschlägt. Im Testfall # 001 sendet der Test Testumgebung 3.4 und 5.7 WCF-Dienst unter Testen und der erwartete Wert 9.1 empfängt. Test Case # 002 ist eine absichtliche, vermeidbare Fehler nur Demonstrations Zwecken.
In den folgenden Abschnitten, beschreibe ich zunächst kurz den WCF-Dienst unter Testbedingungen in dargestellt Abbildung 1 damit Sie verstehen müssen, welche Faktoren von Bedeutung sind, beim Erstellen von WCF socketbasierte Testautomatisierung. Erläutern Sie anschließend ich kurz den Demo-Webclient, die Ihnen Einblick in Wenn die Tests socketbasierte besser geeignet als alternative Techniken ist. Erläutern Sie anschließend ich ausführlich den Code, der die Testumgebung, erstellt so dass Sie möglicherweise werden das Verfahren, die ich hier vorstellen, an Ihre eigenen Bedürfnisse anpassen. In diesem Artikel wird davon ausgegangen, dass fortgeschrittene Anwendungsebene c# Codierfähigkeiten vorhanden ist.
Abbildung 3 WCF Testumgebung ausführen
Die WCF-Dienst unter Test
Visual Studio 2008 verwendet zum Erstellen von WCF-Dienst unter Testbedingungen. Eine praktische Features über WCF-Dienste ist, dass Sie in viele verschiedene Arten von Anwendungen gehostet werden können. Ich habe mich entschieden, Host die WCF MathService in einer Windows Forms-Anwendung zu verarbeiten, aber die Techniken, die ich in diesem Artikel Arbeit mit einem beliebigen Typ von WCF-Host vorhanden.
Nach dem Erstellen einer leeren Windows Forms, hinzugefügt habe ich zwei Button-Steuerelemente und ein ListBox-Steuerelement. Anschließend fügte ich direkt unterhalb der Form-Definition den einfachen Code zum Deklarieren eines WCF-Diensts erforderlich:
[ServiceContract]
public interface IMathService {
[OperationContract]
double Sum(double x, double y);
}
ServiceContract-Attribut, das auf einer Schnittstelle angewendet generiert den Code, die für eine Schnittstelle Formularvorlagenkonverter zu WCF benötigt. Wenn Sie WCF aus Webdiensten verschieben, können Sie ein OperationContract-Attribut, vergleichbar mit dem WebMethod-Attribut vorstellen. Implementieren von WCF-Dienst ist einfach:
public class MathService : IMathService {
public double Sum(double x, double y) {
double answer = x + y;
return answer;
}
}
Mit meinem WCF-Grundstruktur Ort fügte ich einen Verweis auf System.ServiceModel.dll, .NET Framework-Assembly, die WCF-Funktionalität, um das Projekt enthält. Dann fügte ich mithilfe von Anweisungen, die zwei wichtigsten .NET Namespaces innerhalb der Assembly, die für den WCF-Dienst benötigt:
using System.ServiceModel;
using System.ServiceModel.Description;
Der ServiceModel-Namespace enthält die ServiceHost-Klasse und mehrere Klassen, die WCF-Bindungen definieren. Der Description-Namespace enthält die ServiceMetadataBehavior-Klasse, die ich, verwenden um Informationen über den WCF-Dienst veröffentlichen. Als Nächstes fügte ich Instanziierungslogik Dienst an den Ereignishandler für Button1-Steuerelement:
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");
. . .
Der Schlüsselfaktor hier zu beachten ist, dass einen WSHttpBinding mit WCF-Endpunkt erstellt. WCF-Bindungen sind eine Auflistung, die Informationen zu Secutity und Reliabilty Einstellungen Transportprotokoll und Codierungstyp enthält. Der "WSHttpBinding" ist eine hervorragende Allzweck-Bindung für nicht-Duplex-Kommunikation. In der Standardeinstellung WSHttpBinding verschlüsselte Datenübertragung verwendet, aber hier ich SecurityMode.None angeben, sodass Sie leichter Anforderung / Antwort-Daten sehen können.
Als Nächstes fügen Sie Code, um meine Service über das Hinzufügen eines Dienstverweises Mechanismus für Clients sichtbar zu machen:
ServiceMetadataBehavior smb =
new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
serviceHost.Description.Behaviors.Add(smb);
. . .
Jetzt bin ich nun Code hinzufügen, die den WCF-Dienst startet:
serviceHost.Open();
int count = 0;
while (serviceHost.State !=
CommunicationState.Opened &&
count < 50) {
System.Threading.Thread.Sleep(100);
++count;
}
. . .
Sie verwenden eine einfache aber effektive Möglichkeit, einen Dienst hängt beim Start zu erkennen, indem Sie in eine Verzögerungsschleife 0,1 Sekunden pro Verzögerungen von bis zu maximal 50 Verzögerungen. Nachdem die Verzögerungsschleife beendet wird, der WCF-Dienst wurde gestartet, oder die maximale Anzahl an Verzögerungen erreicht wurde:
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");
Ich habe viele Verknüpfungen nehmen würde nicht, beim Schreiben von Produktionscode dauern, z. B. nicht überprüft, ob das ServiceHost-Objekt bereits geöffnet, ist bevor Sie versuchen, ihn zu öffnen. Die Logik im Ereignishandler für Button2-Steuerelements wird der WCF-MathService geschlossen, und ich, dass dasselbe Muster verwenden, um den Dienst zu starten:
int count = 0;
serviceHost.Close();
while (serviceHost.State !=
CommunicationState.Closed &&
count < 50) {
System.Threading.Thread.Sleep(100);
++count;
}
Eine typische Anwendung WebClient
Zum Erstellen des WCF-Anwendung Webclients in dargestellt verwendet Visual Studio 2008 Abbildung 2. Ich begann, indem Sie auf Datei | neu |-Website. Im Dialogfeld Neue Website ich Microsoft .NET Framework 3.5 abzielen, markiert die Empty Web Site-Vorlage, wählen einen Dateisystem-Speicherort in die Programmiersprache c# und mit dem Namen "Mein Projekts WCFMathServiceWebClient.
Als Nächstes im Projektmappen-Explorer ich mit der rechten Maustaste auf das Projekt geklickt haben und aus dem Kontextmenü die Option neues Element hinzufügen ausgewählt. In der daraufhin angezeigten Dialogfeld ausgewählt ich das Web Form-Element. Ich deaktiviert die Option “ Code in eigener Datei platzieren ”, sodass ich mein Anwendungscode in einer einzigen Datei platzieren konnte.
Nach dem Hinzufügen das Webformular, fügte ich Tags serverseitiges Steuerelement, um sehr einfache Benutzeroberfläche der Webanwendung zu erstellen:
<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" />
Anschließend drücken Sie F5, um Visual Studio zum Erstellen der Anwendung und zum Zulassen der automatischen Erstellung einer Web.config-Datei für mein Projekt auffordern angewiesen. Nachdem ich OK’d die Erstellung der Datei Web.config gestartet ich MathService WCF-Dienst das Clientprojekt finden Sie in den Dienst wäre.
Mit der rechten Maustaste auf das Clientprojekt im Projektmappen-Explorer geklickt haben, und aus dem Kontextmenü die Option Dienstverweis hinzufügen ausgewählt. Klicken Sie im Dialogfeld Hinzufügen eines Dienstverweises eingegebenen Speicherort von den WCF-Dienst (http://localhost:8000/ MyWCFMathService / Service) und dann auf die Schaltfläche Aktualisieren klicken. Da der WCF-Dienst ausgeführt wird und der Dienst Metadaten über sich selbst veröffentlicht, wird den Dienst des Visual Studio-Tools gefunden werden. Ich umbenannt den Namespace-Dienst von der Standardeinstellung ServiceReference1, WCFMathServiceReference.
Hinter den Kulissen fügt Visual Studio Informationen über den WCF-Dienst auf die Datei "Web.config" durch einen <system.serviceModel>-Eintrag erstellen. Dass Posten mit Attribut Bindung ein <endpoint>-Element enthält = ” WsHttpBinding ”, so dass der WebClient-Anwendung wie für die Kommunikation mit den WCF-Dienst bekannt ist. In der Entwurfsansicht ich per Doppelklick auf das Button1-Steuerelement, um den Ereignishandler zu registrieren und Logik für den Zugriff auf den Dienst hinzugefügt:
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);
Der Name der Klasse, die Summe-Operation enthält, ist MathServiceClient – mit anderen Worten, der Namen der Klasse (<a0>MathService</a0>) abgeleitet von der WCF-Vertrag-Schnittstelle (IMathService), mit dem “ Client ” angefügt.
Die Testumgebung
Visual Studio 2008 verwendet auch zum Erstellen einer C#-Konsolenanwendungsprojekt namens WCFTestHarness. Fügte ich diese using-Anweisungen, an den Anfang der Datei Program.cs Visual Studio generiert:
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
Ich benötige Klassen im System.Text-Namespace, um Text in Bytearrays konvertieren, da TCP-Ebene Byte funktioniert. Klassen im System.NET-Namespace zum Erstellen von Objekten, die Abstraktionen von IP-Adressen sind erforderlich. Und den System.Net.Sockets-Namespace, erstellen Sie ein Socketobjekt das eigentliche Senden führt und Empfangen von Operationen. Eine kurze Protokollierung-Nachricht an die Main-Methode hinzugefügt, und legen dann meinen Testfalldaten als ein Array von Zeichenfolgen:
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"
};
. . .
Jeder Test Case-Datenzeichenfolge enthält vier Felder mit Kommas als Trennzeichen: ein Testfall-ID zwei Eingaben auf der Summe-Vorgang, und eine erwartete Antwort-Wert. Verwenden Sie meinen Testfall-Daten die wichtigste Test Testumgebung Verarbeitung Schleife zu erstellen:
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];
. . .
Verwenden Sie die Split-Methode für jede Zeichenfolge in die vier Felder aufteilen.
Anschließend folgt eine der wichtigsten Bestandteile senden Eingaben an einen WCF-Dienst über Sockets. Ich erstelle eine SOAP-Nachricht, wie in Abbildung 4.
Abbildung 4 SOAP-Nachricht zum Testen des WCF-Diensts
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>";
Beachten Sie, dass der Testfall-Eingabewerten, input1 und input2, in der SOAP-Nachricht in einem <sum>-Element am Ende der Nachricht eingebettet sind. Aber wie diese Nachrichtenstruktur nicht so einfach ermittelt werden?
Es gibt mehrere Möglichkeiten, die den erforderlichen SOAP-Nachrichtenstruktur und Daten für einen WCF-Dienst zu ermitteln. Der einfachste Ansatz, und ich verwendet habe, ist ein Netzwerk Datenverkehr Untersuchung Tool z. B. Netmon oder Fiddler verwenden, um Daten zu erfassen, während Sie eine Clientanwendung Übung. Anders gesagt, mit den WCF-Dienst ausgeführt und ein Datenverkehr Capture-Tool ebenfalls ausgeführt (verwendet Fiddler), I Siehe Web Anwendung Clientprogramm gestartet Abbildung 2 und verwendet den Client eine Anforderung an die WCF-Dienst zu senden. Das Netzwerk Datenverkehr Capture-Tool zeigte mir die SOAP-Nachricht, die vom Client an den WCF-Dienst gesendet wurde. Verwendet die Informationen die SOAP-Nachricht in meiner Testumgebung zu gestalten.
Mit SOAP-Nachricht erstellt angezeigt ich als Nächstes meinen Testfall als erwarteten Daten in die Shell eingeben:
Console.WriteLine(
"Test Case : " + caseID);
Console.WriteLine(
"Input : <s:Envelope..." + "<x>" +
input1 + "</x>" + "<y>" + input2 +
"</y>...</s:Envelope>");
Console.WriteLine("Expected : " + expected);
Dann richten ich die IP-Adresse des Ziels MathService WCF-Dienst unter Testbedingungen:
string host = "localhost";
IPHostEntry iphe = Dns.Resolve(host);
IPAddress[] addList = iphe.AddressList;
EndPoint ep = new IPEndPoint(addList[0], 8000);
Die DNS.Resolve-Methode gibt eine Liste der IP-Adressen (kann es mehrere) mit einem Computernamen bestimmten Host als IPHostEntry-Objekt verknüpft ist. Das IPHostEntry-Objekt besitzt eine AddressList-Eigenschaft, die ein Array von Objekten für IP-Adresse ist. Ein EndPoint-Objekt besteht aus einer IP-Adresse-Objekt sowie eine Portnummer. Jetzt kann ich meine Socket erstellen:
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());
Socket-Objekte implementiert die Berkeley-Sockets-Schnittstelle, die eine Abstraktion Mechanismus zum Senden und Empfangen von Datenverkehr im Netzwerk ist. Das erste Argument für die Socket-Konstruktor gibt das Adressierschema an. Hier verwenden Sie InterNetwork, die normale IPv4 ist.
Das zweite Argument für die Socket-Konstruktor gibt an, welche der sechs möglichen Socket-Typen, die Sie erstellen möchten. Stream gibt einen TCP-Socket. Der andere ist Dgram, der für UDP (User Datagram Protocol)-Sockets verwendet wird.
Das dritte Argument für den Socket-Konstruktor gibt an, welche Protokolle von dem Socket unterstützt werden. Die <a0>Socket.Connect</a0>-Methode akzeptiert ein <a1>EndPoint</a1>-Objekt.
Als Nächstes erstelle ich meine Headerinformationen:
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";
Ebenso wie mit der SOAP-Nachricht bestimmt ich die Headerinformationen mithilfe ein Überwachungstool Netzwerkverkehr. Beachten Sie, dass jede Zeile mit einer einzigen \n (Zeilenende)-Token, sondern ein Abschlusszeichen \r\n (Wagenrücklauf, Zeilenvorschub endet) und die letzte Zeile mit einer doppelten \r\n endet. Wie ich die kommentierte Codezeile oben angegeben haben, abhängig von einer Reihe von Faktoren müssen Sie möglicherweise ein Keep-Alive oder Close Argument an die Connection-Headereintrag zu senden. Ebenso muss der SOAPAction-Header kein WSHttpBinding.
Nachdem ich Header und SOAP-Nachricht kombinieren können, die gesamte Nachricht in Bytes zu konvertieren, und Senden der Anforderung:
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);
Die <a0>Socket.Send</a0>-Methode verfügt über mehrere Überladungen. Hier senden ich die gesamte Anforderung, ohne eine spezielle senden oder Empfangen von Optionen. Ich nehmen keine des Rückgabewerts verwenden, aber ich hätte diesen Wert um zu prüfen, dass meine gesamte Nachricht gesendet wurde. Jetzt kann die Antwort vom WCF-Dienst abgerufen werden:
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;
}
Da in den meisten Fällen Sie die Anzahl der Bytes Vorhersagen nicht möglich, die in der Antwort enthalten sein sollen, ist die Idee, erstellen einen Puffer und Teile der Antwort zu lesen, bis die gesamte Antwort verbraucht worden ist. Hier verwenden Sie einen Puffer dieser Größe 512. Wie jede Gruppe von 512 Byte empfangen werden, werden Sie in Text konvertiert und an ein Aggregatergebnis Zeichenfolge angehängt.
Mit der Antwort empfangen überprüfen die Antwort zu ermitteln, ob es sich um den aktuellen Testfall erwarteten Wert enthält:
Console.WriteLine("Response received");
if (entireReceive.IndexOf(expected) >= 0)
Console.WriteLine("Test result : Pass");
else
Console.WriteLine("Test result : **FAIL**");
Der Ansatz, den ich hier verwenden für sehr einfache Testszenarien wirksam ist, aber Sie müssen möglicherweise zusätzlichen Logik hinzufügen, wenn Ihr Szenario testen komplizierter ist. Ich vollständig meiner Testumgebung durch Blockieren Loose enden:
. . .
} // main loop
Console.WriteLine(
"\n=======================================");
Console.WriteLine("\nEnd test run");
} // try
catch (Exception ex)
{
Console.WriteLine("Fatal: " + ex.Message);
}
} // Main()
} // Program
} // ns
Zusammenfassung
Es gibt viele Alternativen zum Testen von WCF, aber der Socket basierenden Ansatz für die Tests von WCF-Dienste ist äußerst flexibel. Da TCP und Sockets Low-Level-Konstruktionen sind, arbeiten die Benutzer in einer Vielzahl von Szenarios, insbesondere wenn Sie in einer technologically heterogenen Umgebung testen. Beispielsweise könnten Sie einen WCF-Dienst auf einem Windows-Plattform von einem nicht-Windows-Client gehostet testen. Obwohl Sie den bestimmten C#-Code ändern, hier auf eine Sprache, die vom Client (i. d. r. C++ oder Perl) unterstützte vorgestellten, müssten, würde das allgemeine Verfahren identisch sein.
Darüber hinaus ist ein Socket basierenden Ansatz für Sicherheitstests (Sie müssen jeden WCF-Dienst gelten für nicht-freundliche Socket Überprüfungen werden annehmen) und Leistungstests (ein Socket-Ansatz ist direkte und bieten Round-Trip Zeitmessungsdaten Basisplan) recht nützlich.
Dr. James McCaffrey arbeitet für Volt Information Sciences Inc., wo er technische Schulungen für Softwareentwickler bei Microsoft Redmond, Washington, Campus verwaltet. Er hat an verschiedenen Microsoft-Produkten, einschließlich Internet Explorer und MSN Search gearbeitet. Dr. McCaffrey ist Autor von “ .NET Test Automation Recipes ” (Apress, 2006) und kann unter erreicht werden jammc@microsoft.com.
Dank an den folgenden technischen Experten für die Überprüfung dieses Artikels: Carlos Figueira