问题描述:有一个Oliver Cafe Shop聊天机器人,如何实现自动化脚本自动测试这个聊天机器人的功能。
实现效果:通过代码来实现客户端发送请求来代替Bot Framework Emulator输入Tea,然后客户端监听和接收服务器端(Bot)发来的回复(图片中选择drinksubtype的卡片),最后根据服务器端(Bot)的回复再次发送请求(红茶)或者进行别的操作。
1.首先建立一个工程TestWebRequest,作为代替模拟器发送请求,具体实现函数代码如下,其中WriteLog在以前的博客中:这里
//Post请求的json格式的content的路径
public List<string> orderDrinkAllJsonPathList = new List<string> { "../../../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<HttpResponseMessage> 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<Activity>(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)的回复请求
/// <summary>/// 应用程序的主入口点。/// </summary>[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<Activity>(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<MSAResponse>(responseString);return result.Access_Token; }}
4.具体源代码:这里