问题描述:有一个Oliver Cafe Shop聊天机器人,如何实现自动化脚本自动测试这个聊天机器人的功能。
实现效果:通过代码来实现客户端发送请求来代替Bot Framework Emulator输入Tea,然后客户端监听和接收服务器端(Bot)发来的回复(图片中选择drinksubtype的卡片),最后根据服务器端(Bot)的回复再次发送请求(红茶)或者进行别的操作。
1.首先建立一个工程TestWebRequest,作为代替模拟器发送请求,具体实现函数代码如下,其中WriteLog在以前的博客中:这里
//Post请求的json格式的content的路径
public List orderDrinkAllJsonPathList = new List { "../../../Json/Order.json" };
public void OrderDrink()
{
WriteLog("Order Drink Begin.");
try
{
foreach (string jsonPath in orderDrinkAllJsonPathList)
{
string content = File.ReadAllText(jsonPath);
string jsonPathName = jsonPath.Substring(jsonPath.LastIndexOf("/") + 1);
HttpResponseMessage response = OrderDrinkOneStep(content, jsonPathName).Result;
//HttpResponseMessage response = GetActivity(content, jsonPathName).Result;
if (response != null && response.IsSuccessStatusCode)
{
string responseContent = response.Content.ReadAsStringAsync().Result ?? "Empty";
WriteLog("Order Drink " + jsonPathName + " successful:" + responseContent);
//System.Threading.Thread.Sleep(3000 * timeCount * 3);
Console.WriteLine("Order Drink " + jsonPathName + " successful:" + responseContent);
}
else if (response != null)
{
WriteLog("Order Drink " + jsonPathName + " failed:" + "ReasonPhrase: " + response.ReasonPhrase);
Console.WriteLine("Order Drink " + jsonPathName + " failed:" + "ReasonPhrase: " + response.ReasonPhrase);
break;
}
}
WriteLog("Order Drink End.");
}
catch (Exception ex)
{
WriteLog("Order Drink " + " failed:" + "Exception: " + ex.Message);
}
}
具体的OrderDrink的每一个具体步骤只是request的content不一样,所以这样写进一个方法提高代码利用率
public async Task OrderDrinkOneStep(string content, string errorJsonPath)
{
HttpResponseMessage response = null;
//跳过证书认证,可以不用
var httpClientHandler = new HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, error) => { return true; };
using (HttpClient client = new HttpClient(httpClientHandler))
{
try
{
var requestObj = JsonConvert.DeserializeObject(content);
StringContent strContent = new StringContent(JsonConvert.SerializeObject(requestObj), Encoding.UTF8, "application/json");
var request = new HttpRequestMessage()
{
RequestUri = new Uri(requestUrl),
Method = HttpMethod.Post,
Content = strContent
};
//本地创建bot不需要app auth认证,详细的在后面介绍
//string token = GetMSAJWTToken("YourAppId", "YouAppPassword");
//string authorization = "Bearer " + token;
//request.Headers.Add("Authorization", authorization);
response = await client.SendAsync(request);
}
catch (Exception e)
{
string logContent = "RestoreFileOneStep" + "errorJsonPath " + errorJsonPath + "errorMessage: " + e.Message;
WriteLog(logContent);
}
}
return response;
}
其中Order.json文件的内容如下,直接复制第一张图片里面的json文本即可,注意改一下serviceurl的端口号,后面会监听这个端口号(7777致敬一下clearlove哈哈哈哈哈哈)
{
"channelData": {
"clientActivityID": "15584188744740.9l71xdomgu6",
"state": "sent"
},
"channelId": "emulator",
"conversation": {
"id": "C743EA22-CE36-4409-A38C-FD6FF99ECE1C|livechat"
},
"entities": [
{
"requiresBotState": true,
"supportsListening": true,
"supportsTts": true,
"type": "ClientCapabilities"
}
],
"from": {
"id": "c9a42e69-b5fe-48c4-b24e-efdc50e6af34",
"name": "User",
"role": "user"
},
"id": "c5c38b50-7b8e-11e9-bcd2-17e98060b0ce",
"localTimestamp": "2019-05-21T14:07:54+08:00",
"locale": "en-us",
"recipient": {
"id": "1",
"name": "Bot",
"role": "bot"
},
"serviceUrl": "http://localhost:7777",
"showInInspector": true,
"text": "Tea",
"textFormat": "plain",
"timestamp": "2019-05-21T06:07:54.501Z",
"type": "message"
}
运行一下TestWebRequest,暂时把serviceUrl的端口号改回去,这样就会在模拟器上就会得到模拟器输入"Tea"相同的结果
2.创建一个项目Client来代码模拟器来监听服务器端(Bot)的回复请求
///
/// 应用程序的主入口点。
///
[STAThread]
static void Main(string[] args)
{
int recv;//用于表示客户端发送的信息长度
string ipadd = "127.0.0.1";
int port = 7777;
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ipadd), port);
Socket newsock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
newsock.Bind(ipep);//绑定
newsock.Listen(30);//监听
Console.WriteLine("waiting for a client");
Socket client = newsock.Accept();//当有可用的客户端连接尝试时执行,并返回一个新的socket,用于与客户端之间的通信
IPEndPoint clientip = (IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("connect with client:" + clientip.Address + " at port:" + clientip.Port);
try
{
while (true)
{//用死循环来不断的从客户端获取信息
var data = new byte[client.ReceiveBufferSize];
recv = client.Receive(data);
if (recv == 0)//当信息长度为0,说明客户端连接断开
break;
string requestBody = System.Text.Encoding.Default.GetString(data);
//得到服务器返回的内容,就可以继续进行后面的测试输入了
//requestBody = requestBody.Substring(requestBody.IndexOf("{"));
//Activity activity = JsonConvert.DeserializeObject(requestBody);
//TestBotRequest.WebRequest webRequest = new TestBotRequest.WebRequest();
//webRequest.CreatePostActivity(activity);
}
}
catch (Exception ex)
{
WriteLog(ex.Message);
}
Console.WriteLine("Disconnected from" + clientip.Address);
client.Close();
newsock.Close();
Console.ReadKey();
}
3.启动bot,这里不用虚拟器打开bot,然后启动Client和TestWebRequest,就完成了一轮自动化脚本实现的对话了。这里VS创建的bot不需要app auth认证的,因为appid是空,其他部署到Azure需要认证的话需要在request的header加上token,token的具体获取方法如下,
public string GetMSAJWTToken(string clientId,string clientSecret)
{
var authendpoint = "https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token";
using (var client = new WebClient())
{
var values = new NameValueCollection();
values["grant_type"] = "client_credentials";
values["client_id"] = clientId;
values["client_secret"] = clientSecret;
values["scope"] = clientId + "/.default";
var response = client.UploadValues(authendpoint, values);
var responseString = Encoding.Default.GetString(response);
var result = JsonConvert.DeserializeObject(responseString);
return result.Access_Token;
}
}
4.具体源代码:这里