i am currently working on automated unit tests inside the Microsoft Bot Framework 4. From there, i want to check simple conversational statements from the bot. In the CoreBot Tests sample (https://learn.microsoft.com/en-us/azure/bot-service/unit-test-bots?view=azure-bot-service-4.0&tabs=csharp) is demonstrated how it is possible to do that but for me, my bot isnt using dialogs (as far as i know).
My question here is now, how can i unit test simple Question/Answer Statements? My main goal is to unit test my QnA Maker Knowledge Bases.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using System;
using Newtonsoft.Json;
using System.IO;
using Microsoft.Bot.Builder.AI.QnA;
using Microsoft.Extensions.Configuration;
namespace SEKAI.Bots
{
public class DispatchBot : ActivityHandler
{
private ILogger<DispatchBot> _logger;
private IBotServices _botServices;
private IConfiguration _configuration;
public DispatchBot(IConfiguration configuration, IBotServices botServices, ILogger<DispatchBot> logger)
{
_configuration = configuration;
_logger = logger;
_botServices = botServices;
}
protected async Task NoMatchFound(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var table = BotUtility.BotUtility.GetTableReference(_configuration);
BotUtility.BotUtility.InsertRecord(turnContext, table);
// Wird ausgeführt, wenn keine KnowledgeBase gefunden wird
System.Diagnostics.Debug.WriteLine("### FINDE KEINE PASSENDE ANTWORT ###");
await turnContext.SendActivityAsync(MessageFactory.Text("Leider kann ich Ihnen hierbei noch nicht weiterhelfen. Ich bin aber schon dabei, Neues zu lernen!"), cancellationToken);
}
// Wenn der Benutzer den Chat startet
protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Name == "webchat/join")
{
string WelcomeCardpath = Path.Combine(".", "AdaptiveCards", "WelcomeCard.json");
var cardAttachment = BotUtility.BotUtility.CreateAdaptiveCard(WelcomeCardpath);
await turnContext.SendActivityAsync(MessageFactory.Attachment(cardAttachment), cancellationToken);
}
}
// Wenn ein Nutzer eine Nachricht schreibt
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
// First, we use the dispatch model to determine which cognitive service (LUIS or QnA) to use.
var recognizerResult = await _botServices.Dispatch.RecognizeAsync(turnContext, cancellationToken);
// Top intent tell us which cognitive service to use.
var topIntent = recognizerResult.GetTopScoringIntent();
// Next, we call the dispatcher with the top intent.
await DispatchToTopIntentAsync(turnContext, topIntent.intent, recognizerResult, cancellationToken);
}
// Suche nach der richtigen KnowledgeBase
private async Task DispatchToTopIntentAsync(ITurnContext<IMessageActivity> turnContext, string intent, RecognizerResult recognizerResult, CancellationToken cancellationToken)
{
switch (intent)
{
case "q_SEKAI_Chitchat":
await ProcessSEKAI_ChitchatAsync(turnContext, cancellationToken);
break;
default:
// Wird ausgeführt, wenn keine KnowledgeBase gefunden wird
_logger.LogInformation($"Dispatch unrecognized intent: {intent}.");
await NoMatchFound(turnContext, cancellationToken);
break;
}
}
// Bearbeitung aus SEKAI_Chitchat
private async Task ProcessSEKAI_ChitchatAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
_logger.LogInformation("ProcessSEKAI_ChitchatAsync");
// Confidence Score zur KnowledgeBase
var metadata = new Metadata();
var qnaOptions = new QnAMakerOptions();
qnaOptions.ScoreThreshold = 0.70F;
// Speichere die Antwort aus der KnowledgeBase
var results = await _botServices.SEKAI_Chitchat.GetAnswersAsync(turnContext, qnaOptions);
if (results.Any())
{
// Speichere die Antwort aus der KnowledgeBase für die Schlüsselwort-Verarbeitung
string savetext = results.First().Answer;
System.Diagnostics.Debug.WriteLine(savetext);
if (savetext.Contains("{card}"))
{
// Hier wird das Schlüsselwort für die Antwortausgabe entfernt
int firstKeyword = savetext.IndexOf("{card}") + "{card}".Length;
int lastKeyword = savetext.LastIndexOf("{/card}");
string subsavetext = savetext.Substring(firstKeyword, lastKeyword - firstKeyword);
System.Diagnostics.Debug.WriteLine(subsavetext);
// Ausgabe der Adaptive Card
savetext = savetext.Replace("{card}" + subsavetext + "{/card}", "");
string AdaptiveCardPath = Path.Combine(".", "AdaptiveCards", subsavetext + ".json");
var cardAttachment = BotUtility.BotUtility.CreateAdaptiveCard(AdaptiveCardPath);
await turnContext.SendActivityAsync(MessageFactory.Attachment(cardAttachment), cancellationToken);
// Ausgabe von Text
await turnContext.SendActivityAsync(MessageFactory.Text(savetext), cancellationToken);
}
else
{
// Befindet sich in der Datenbank kein Schlüsselwort, wird nur die Antwort ausgegeben
await turnContext.SendActivityAsync(MessageFactory.Text(savetext), cancellationToken);
}
}
else
{
// Wird ausgegeben, wenn keine Antwort in der KnowledgeBase passt
await NoMatchFound(turnContext, cancellationToken);
}
}
}
}
The bot i have build is based completly on the NLP with Dispatch sample (https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/14.nlp-with-dispatch).
I have already modified the bot a bit and thats why i added the main file (Dispatchbot.cs file in the GitHub repo) for my version.