8.4 使用TCP测试Web方法
问题
如何使用TCP通过调用Web service中的某个Web方法对其进行测试。
设计
第一步,实例化一个TcpClient对象并连接到Web service所在的远程服务器。第二步,构造一个用于发送给Web方法的SOAP消息。第三步,构造一个包含HTTP信息的报头。第四步,实例化一个与TcpClient对象相关联的NetworkStream对象,并通过NetworkStream.Write()方法发送报头和SOAP消息。第五步,通过在while循环里使用NetworkStream.Read()方法接收SOAP响应。第六步,根据期望值对SOAP响应进行分析。
方案
这个例子通过TCP把字符串“testing”发送给Web方法GetTitles()。
Console.WriteLine("Calling Web Method GetTitles() using TCP");
string input = "testing";
//TcpClient client = new TcpClient("127.0.0.1", 80);
TcpClient client = new TcpClient(AddressFamily.InterNetwork);
client.Connect("127.0.0.1", 80);
string soapMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
soapMessage += "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-
instance\"";
soapMessage += " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
soapMessage += " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">";
soapMessage += "<soap:Body>";
soapMessage += "<GetTitles xmlns=\"http://tempuri.org/\">";
soapMessage += "<filter>" + input + "</filter>";
soapMessage += "</GetTitles>";
soapMessage += "</soap:Body>";
soapMessage += "</soap:Envelope>";
string webService = "/TestAuto/Ch8/TheWebService/BookSearch.asmx";
string host = "localhost";
string webMethod = "GetTitles";
string header = "POST " + webService + " HTTP/1.1\r\n";
header += "Host: " + host + "\r\n";
header += "Content-Type: text/xml; charset=utf-8\r\n";
header += "Content-Length: " + soapMessage.Length.ToString() + "\r\n";
header += "Connection: close\r\n";
header += "SOAPAction: \"http://tempuri.org/" + webMethod + "\"\r\n\r\n";
Console.Write("Header is: \n" + header);
string requestAsString = header + soapMessage;
byte[] requestAsBytes = Encoding.ASCII.GetBytes(requestAsString);
NetworkStream stream = client.GetStream();
stream.Write(requestAsBytes, 0, requestAsBytes.Length);
byte[] responseBufferAsBytes = new byte[512];
string responseAsString = "";
string entireResponse = "";
int numBytesReceived = 0;
while ((numBytesReceived = stream.Read(responseBufferAsBytes, 0, 512)) > 0)
{
responseAsString = Encoding.ASCII.GetString(responseBufferAsBytes, 0,
numBytesReceived);
entireResponse += responseAsString;
}
Console.WriteLine(entireResponse);
if ( entireResponse.IndexOf("002") >= 0 &&
entireResponse.IndexOf("004") >= 0 &&
entireResponse.IndexOf("005") >= 0 )
Console.WriteLine("\nPass");
else
Console.WriteLine("\nFail");
通过TCP调用Web方法与通过套接字调用Web方法非常类似。这并不是一种巧合,因为TcpClient原本就是被设计用来作为Socket类的一个更为友好的外覆类(wrapper)。这两种技术非常相似,以至于很难选择该用哪一个。有两种方法可以帮助我们判断应该使用哪一种技术。一方面,因为TcpClient相比于Socket类较为简洁,所以当通过TCP调用Web service的时候,应该使用TcpClient,而当调用那些由非TCP协议实现的Web service时,应该使用Socket。从实践上来看,因为ASP.NET Web service使用HTTP协议,而HTTP协议又使用TCP协议,所以我们可以简单地说:“当对Web方法进行底层调用的时候,总是使用TcpClient类”。另一方面,因为使用TcpClient与使用Socket非常相似,而且Socket更为灵活,因此你可能也要用到Socket类。从而这一条也可以简化成“当对Web方法进行底层调用的时候,总是使用Socket类”。最终的结论是:用两种方法进行测试比只选用其中一种要好。但是,因为使用TcpClient与使用Socket类非常相似,所以当我们试图通过两种方式调用Web方法来对其进行测试,从而验证自动化测试程序时,应该只使用TcpClient类或Socket类中的一种,而不是两种都用上。
注解
用于测试ASP.NET Web service的4种主要方法中,使用TCP这种方式,其操作所在的抽象层次仅比使用套接字稍高。通过TcpClient类对Web方法进行测试的时候,有6个分开的步骤。这6个步骤都可以单独地看作是“问题及其解决方案”,但是因为这些步骤之间的相互依赖性很强,因此放在一起讲述更容易理解。第一步是实例化一个TcpClient对象并连接到待测Web service所在的服务器:
TcpClient client = new TcpClient(AddressFamily.InterNetwork);
client.Connect("127.0.0.1", 80);
关于AddressFamily枚举值的讨论请参见8.2节。创建好TcpClient对象之后,就可以通过指定IP地址和端口号连接到服务器。也可以直接把服务器IP地址和端口号传给TcpClient重载过的一个构造函数,从而省去指定AddressFamily,因为InterNetwork是Connect()方法的默认值。
TcpClient client = new TcpClient("127.0.0.1", 80);
通过TCP调用Web方法,第二步是构造一个用于发送给Web方法的SOAP消息:
string soapMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
soapMessage += "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-
instance\"";
soapMessage += " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
soapMessage += " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">";
soapMessage += "<soap:Body>";
soapMessage += "<GetTitles xmlns=\"http://tempuri.org/\">";
soapMessage += "<filter>" + input + "</filter>";
soapMessage += "</GetTitles>";
soapMessage += "</soap:Body>";
soapMessage += "</soap:Envelope>";
如8.2节所述,我们可以从Visual Studio中得到一个SOAP消息模板。第三步是构造一个包含HTTP信息的报头:
string webService = "/TestAuto/Ch8/TheWebService/BookSearch.asmx";
string host = "localhost";
string webMethod = "GetTitles";
string header = "POST " + webService + " HTTP/1.1\r\n";
header += "Host: " + host + "\r\n";
header += "Content-Type: text/xml; charset=utf-8\r\n";
header += "Content-Length: " + soapMessage.Length.ToString() + "\r\n";
header += "Connection: close\r\n";
header += "SOAPAction: \"http://tempuri.org/" + webMethod + "\"\r\n\r\n";
Console.Write("Header is: \n" + header);
这些信息,仍然可以从Visual Studio中得到。第四步是实例化一个与TcpClient对象相关联的NetworkStream对象,并通过NetworkStream.Write()方法发送报头和SOAP消息:
string requestAsString = header + soapMessage;
byte[] requestAsBytes = Encoding.ASCII.GetBytes(requestAsString);
NetworkStream stream = client.GetStream();
stream.Write(requestAsBytes, 0, requestAsBytes.Length);
与使用Socket类相比,第四步的差别是最大的。通过GetBytes()方法把HTTP报头和SOAP消息转换成字节数组之后,我们通过TcpClient.GetStream()方法创建一个NetworkStream对象,然后使用NetworkStream.Write()把数据通过网络发送出去——这个过程非常简单明了。通过TCP调用Web方法,第五步是在while循环里使用NetworkStream.Read()方法接收SOAP响应:
byte[] responseBufferAsBytes = new byte[512];
string responseAsString = "";
string entireResponse = "";
int numBytesReceived = 0;
while ((numBytesReceived = stream.Read(responseBufferAsBytes, 0, 512)) > 0)
{
responseAsString = Encoding.ASCII.GetString(responseBufferAsBytes, 0,
numBytesReceived);
entireResponse += responseAsString;
}
这一步遵循与8.2节所描述的几乎一样的读取模式。把两段代码对照阅读可能更容易理解。一旦理解了这种通用模式,你会发现无论是在自动化测试还是程序开发中,它都被广泛使用。第六步,也就是最后一步,是根据给定的期望值检查SOAP响应:
Console.WriteLine(entireResponse);
if ( entireResponse.IndexOf("002") >= 0 &&
entireResponse.IndexOf("004") >= 0 &&
entireResponse.IndexOf("005") >= 0 )
Console.WriteLine("\nPass");
else
Console.WriteLine("\nFail");
正如8.2节和8.3节所讨论的那样,当测试结果的返回值比较复杂的时候(比如这里),要判断测试结果通过与否并不是很容易。







