Bot Framework와 DirectLine을 이용한 통합 테스트 (1)
저는 회사에 잠시 입사한 이후 Bot Framework 통합 테스트 업무를 맡게 되었습니다.
그룹에서 제가 하는 일은 봇을 최대한 안정적으로 만드는 것인데, 먼저 Bot Framework가 무엇인가요?
지능형 봇을 구축, 연결, 배포 및 관리하여 웹사이트, 앱, Cortana, Microsoft Teams, Skype, Slack, Facebook Messenger 등에서 사용자와 자연스럽게 상호 작용합니다. 사용한 만큼만 비용을 지불하면서 완전한 봇 구축 환경을 빠르게 시작하세요.
Bot Framework에 대한 자세한 내용은 여기에서 확인할 수 있습니다.
다음 설명에서는 Bot Framework 작동 방식에 대한 모든 기본 정보를 다루지 않습니다. 이해가 되지 않는 경우 공식 문서를 확인하세요.
통합 테스트는 왜 필요한가요?
통합 테스트가 필요한 이유는 동료 중 한 명이 수정 사항, 새 기능 또는 새 버그를 푸시할 때마다 이 테스트가 코드를 프로덕션 환경으로 푸시하기 전에 실행되며, 테스트 중 하나라도 실패하면 코드가 프로덕션 환경으로 이동하지 않기 때문입니다. 즉, 최종 사용자에게는 버그가 발생하지 않습니다.
봇에서의 통합 테스트?
저는 봇에서는 통합 테스트가 매우 중요하다고 믿습니다. 일부 메뉴가 작동하지 않거나 일부 기능이 아무것도 반환하지 않는 봇을 가질 수 없습니다.
회사는 사람들이 문제로 바쁘게 지내는 것을 원하지 않기 때문에 고객을 위해 봇을 사용하고 있습니다. 봇이 사용자를 도울 수 있다면 다른 직원은 자신의 시간을 더 중요한 일에 사용할 수 있습니다.
솔루션 개요.
이 작업을 수행하기 위해 Visual Studio에서 API Rest용 WebClient와 케이스를 저장할 Json 파일을 사용하는 테스트 프로젝트를 사용했습니다.
JSON 파일
{
"secret": "direct-line-secret",
"directlineGenerateTokenEndpoint": "https://directline.botframework.com/v3/directline/tokens/generate",
"directlineConversationEndpoint": "https://directline.botframework.com/v3/directline/conversations/",
"entries": [
{
"name": "DecirHola",
"request": {
"type": "message",
"text": "Hola",
"from": {
"id": "default-user",
"name": "User"
},
"locale": "es",
"textFormat": "plain",
"timestamp": "2018-04-09T08:04:37.195Z",
"channelData": {
"clientActivityId": "1523261059363.6264723268323733.0"
},
"entities": [
{
"type": "ClientCapabilities",
"requiresBotState": true,
"supportsTts": true,
"supportsListening": true
}
],
"id": "61hacck8j6jg"
},
"response": {
"type": "message",
"timestamp": "2018-04-09T08:04:37.901Z",
"localTimestamp": "2018-04-09T09:04:37+01:00",
"serviceUrl": "http://localhost:50629",
"channelId": "emulator",
"from": {
"id": "j98bbdf097a",
"name": "Bot"
},
"conversation": {
"id": "eabcie4be8ak"
},
"recipient": {
"id": "default-user"
},
"locale": "es",
"text": "No tengo respuesta para eso.",
"attachments": [],
"entities": [],
"replyToId": "61hacck8j6jg",
"id": "47me557ikbf7"
},
"assert": "Request.Text == Response.Text"
}
]
}
보시다시피 우리는 다음을 가지고 있습니다:
- 다이렉트라인 비밀 -> 게시된 봇의 비밀
- Directline 토큰 생성 엔드포인트 -> 시크릿을 사용하여 토큰을 가져오는 엔드포인트
- 직접 대화 끝점 -> 대화를 플레이하기 위한 끝점
- 항목 -> 테스트 케이스
- 요청 -> 대화에 보내는 내용
- 응답 -> 우리가 얻기를 기대하는 것
- 주장 -> 무엇을 비교하고 있나요?
역직렬화
완벽하게 형식화된 json 파일이 있으므로 이제 이를 솔루션에 로드해야 하므로 JSON.NET과 일부 클래스를 사용하게 됩니다. 먼저 모든 것을 포함하는 항목 컬렉션이 있고 각 컬렉션에 대한 항목 목록이 있습니다.
/// <summary>
/// Object to parse from the file
/// </summary>
public class TestEntriesCollection
{
/// <summary>
/// DirectLine Secret
/// </summary>
[JsonProperty("secret")]
public string Secret { get; set; }
/// <summary>
/// Endpoint to get the token using the secret for DirectLine
/// </summary>
[JsonProperty("directlineGenerateTokenEndpoint")]
public string DirectLineGenerateTokenEndpoint { get; set; }
/// <summary>
/// Endpoint for a conversation in DirectLine
/// </summary>
[JsonProperty("directlineConversationEndpoint")]
public string DirectLineConversationEndpoint { get; set; }
/// <summary>
/// Entries list
/// </summary>
[JsonProperty("entries")]
public List<TestEntry> Entries { get; set; }
}
그리고 이것은 테스트 케이스에 대한 이름, 요청, 응답 및 주장을 포함하는 객체입니다.
public class TestEntry
{
/// <summary>
/// Entry name
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Activity requested by the entry
/// </summary>
[JsonProperty("request")]
public Activity Request { get; set; }
/// <summary>
/// Activity response expected by the entry
/// </summary>
[JsonProperty("response")]
public Activity Response { get; set; }
/// <summary>
/// Assert value in string
/// </summary>
[JsonProperty("assert")]
public string Assert { get; set; }
}
테스트 케이스의 json에서 객체로 구문 분석
구문 분석 개체에 대한 클래스가 있으면 개체로 읽어야 하므로 매우 쉽습니다.
// Load entries from file
var path = System.IO.File.ReadAllText(@"C:\data.json");
// Deserialize to object
var data = JsonConvert.DeserializeObject<TestEntriesCollection>(path);
이제 이 컬렉션을 사용하면 컬렉션을 반복하여 foreach 등을 사용하여 정보를 얻을 수 있습니다.
foreach (TestEntry entry in data.Entries)
{
....
}
이것이 이 부분의 전부입니다. 다음 부분에는 DirectLine에 대한 인증이 포함됩니다. 모든 코드는 내 github의 this 저장소에 저장되어 있습니다.