Durchführen von Komponententests für Bots

GILT FÜR: SDK v4

In diesem Thema wird Folgendes beschrieben:

  • Erstellen von Unittests für Bots.
  • Verwenden einer Bestätigung für einen Abgleich der Aktivitäten, die von einem Dialog-Turn zurückgegeben werden, mit den erwarteten Werten.
  • Verwenden einer Bestätigung zum Überprüfen der Ergebnisse, die von einem Dialog zurückgegeben werden.
  • Erstellen unterschiedlicher Arten von datengesteuerten Tests.
  • Erstellen von Pseudoobjekten für die verschiedenen Abhängigkeiten eines Dialogs, z. B. Sprach-Erkennungsmodule usw.

Voraussetzungen

Im CoreBot.Tests-Beispiel dieses Themas wird zum Erstellen von Komponententests auf das Microsoft.Bot.Builder.Testing-Paket, XUnit und Moq verwiesen.

Das Core-Bot-Beispiel verwendet Language Understanding (LUIS), um Benutzerabsichten zu identifizieren. Die Identifizierung von Benutzerabsichten ist jedoch nicht der Fokus dieses Artikels. Informationen zum Identifizieren von Benutzerabsichten finden Sie unter Natürliches Sprachverständnis und Hinzufügen von natürlichem Sprachverständnis zu Ihrem Bot.

Hinweis

Language Understanding (LUIS) wird am 1. Oktober 2025 eingestellt. Ab dem 1. April 2023 können Sie keine neuen LUIS-Ressourcen erstellen. Eine neuere Version von Language Understanding ist jetzt als Teil von Azure KI Language verfügbar.

Conversational Language Understanding (CLU), ein Feature von Azure KI Language, ist die aktualisierte Version von LUIS. Weitere Informationen zur Unterstützung von Language Understanding im Bot Framework SDK finden Sie unter Natürliches Sprachverständnis.

Testen von Dialogen

Im CoreBot-Beispiel werden die Komponententests für die Dialoge mit der DialogTestClient-Klasse durchgeführt. Diese Klasse verfügt über einen Mechanismus für das isolierte Testen außerhalb eines Bots, ohne dass Ihr Code über einen Webdienst bereitgestellt werden muss.

Bei Verwendung dieser Klasse können Sie Komponententests schreiben, mit denen Dialogantworten Turn für Turn überprüft werden. Komponententests mit der DialogTestClient-Klasse sollten auch für andere Dialoge funktionieren, die mit der botbuilder-Dialogbibliothek erstellt werden.

Im folgenden Beispiel werden Tests veranschaulicht, die von DialogTestClient abgeleitet sind:

var sut = new BookingDialog();
var testClient = new DialogTestClient(Channels.Msteams, sut);

var reply = await testClient.SendActivityAsync<IMessageActivity>("hi");
Assert.Equal("Where would you like to travel to?", reply.Text);

reply = await testClient.SendActivityAsync<IMessageActivity>("Seattle");
Assert.Equal("Where are you traveling from?", reply.Text);

reply = await testClient.SendActivityAsync<IMessageActivity>("New York");
Assert.Equal("When would you like to travel?", reply.Text);

reply = await testClient.SendActivityAsync<IMessageActivity>("tomorrow");
Assert.Equal("OK, I will book a flight from Seattle to New York for tomorrow, Is this Correct?", reply.Text);

reply = await testClient.SendActivityAsync<IMessageActivity>("yes");
Assert.Equal("Sure thing, wait while I finalize your reservation...", reply.Text);

reply = testClient.GetNextReply<IMessageActivity>();
Assert.Equal("All set, I have booked your flight to Seattle for tomorrow", reply.Text);

Die DialogTestClient-Klasse ist im Microsoft.Bot.Builder.Testing-Namespace definiert und im NuGet-Paket Microsoft.Bot.Builder.Testing enthalten.

DialogTestClient

Der erste Parameter von DialogTestClient ist der Zielkanal. Hiermit können Sie unterschiedliche Renderinglogik basierend auf dem Zielkanal für Ihren Bot testen (Teams, Slack und so weiter). Falls Sie in Bezug auf Ihren Zielkanal unsicher sind, können Sie die Kanal-IDs Emulator oder Test verwenden. Beachten Sie aber, dass sich einige Komponenten je nach aktuellem Kanal ggf. unterschiedlich verhalten. Mit ConfirmPrompt werden die Ja/Nein-Optionen für die Kanäle Test und Emulator beispielsweise unterschiedlich gerendert. Sie können diesen Parameter auch nutzen, um die bedingte Renderinglogik in Ihrem Dialog anhand der Kanal-ID zu testen.

Der zweite Parameter ist eine Instance des zu testenden Dialogs. In dem Beispielcode in diesem Artikel steht sut für das zu prüfende System.

Mit dem Konstruktor DialogTestClient werden zusätzliche Parameter angegeben, damit Sie das Clientverhalten weiter anpassen oder bei Bedarf Parameter an den zu testenden Dialog übergeben können. Sie können Initialisierungsdaten für den Dialog übergeben, benutzerdefinierte Middleware hinzufügen oder Ihren eigenen TestAdapter und eine ConversationState-Instanz verwenden.

Senden und Empfangen von Nachrichten

Mit der SendActivityAsync<IActivity>-Methode können Sie eine Textäußerung oder ein IActivity-Element an Ihren Dialog senden. Die Methode gibt die erste empfangene Nachricht zurück. Der Parameter <T> wird verwendet, um eine stark typisierte Instanz der Antwort zurückzugeben, damit Sie sie bestätigen können, ohne sie umwandeln zu müssen.

var reply = await testClient.SendActivityAsync<IMessageActivity>("hi");
Assert.Equal("Where would you like to travel to?", reply.Text);

In einigen Szenarien sendet Ihr Bot in einer Antwort an eine Aktivität unter Umständen mehrere Nachrichten. Die Antworten werden von DialogTestClient dann in die Warteschlange eingereiht, und Sie können die GetNextReply<IActivity>-Methode verwenden, um die nächste Nachricht aus der Antwortwarteschlange abzurufen.

reply = testClient.GetNextReply<IMessageActivity>();
Assert.Equal("All set, I have booked your flight to Seattle for tomorrow", reply.Text);

GetNextReply<IActivity> gibt „null“ zurück, falls die Antwortwarteschlange keine weiteren Nachrichten enthält.

Bestätigen von Aktivitäten

Im Code des CoreBot-Beispiels wird nur die Text-Eigenschaft der zurückgegebenen Aktivitäten bestätigt. Bei komplexeren Bots kann es ratsam sein, auch andere Eigenschaften zu bestätigen, z. B. Speak, InputHint, ChannelData usw.

Assert.Equal("Sure thing, wait while I finalize your reservation...", reply.Text);
Assert.Equal("One moment please...", reply.Speak);
Assert.Equal(InputHints.IgnoringInput, reply.InputHint);

Um dies zu erreichen, können Sie jede Eigenschaft wie oben gezeigt einzeln überprüfen. Sie können Ihre eigenen Hilfsprogramme zum Bestätigen von Aktivitäten schreiben, oder Sie können andere Frameworks verwenden, z. B. FluentAssertions, um benutzerdefinierte Assertionen zu schreiben und Ihren Testcode zu vereinfachen.

Übergeben von Parametern an Ihre Dialoge

Der Konstruktor DialogTestClient verfügt über ein initialDialogOptions-Element, das verwendet werden kann, um Parameter an Ihren Dialog zu übergeben. Mit dem MainDialog-Element in diesem Beispiel wird beispielsweise ein BookingDetails-Objekt aus den Spracherkennungsergebnissen mit den Entitäten initialisiert, die aus der Äußerung des Benutzers aufgelöst werden. Dieses Objekt wird dann im Aufruf übergeben, um BookingDialog aufzurufen.

Sie können dies in einem Test wie folgt implementieren:

var inputDialogParams = new BookingDetails()
{
    Destination = "Seattle",
    TravelDate = $"{DateTime.UtcNow.AddDays(1):yyyy-MM-dd}"
};

var sut = new BookingDialog();
var testClient = new DialogTestClient(Channels.Msteams, sut, inputDialogParams);

Das BookingDialog-Element empfängt diesen Parameter und greift im Test genauso darauf zu, als ob es über MainDialog aufgerufen wird.

private async Task<DialogTurnResult> DestinationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    var bookingDetails = (BookingDetails)stepContext.Options;
    ...
}

Bestätigen der Ergebnisse von Dialog-Turns

Einige Dialoge, z. B. BookingDialog oder DateResolverDialog, geben einen Wert an den aufrufenden Dialog zurück. Das DialogTestClient-Objekt macht eine DialogTurnResult-Eigenschaft verfügbar, die zum Analysieren und Bestätigen der vom Dialog zurückgegebenen Ergebnisse verwendet werden kann.

Beispiel:

var sut = new BookingDialog();
var testClient = new DialogTestClient(Channels.Msteams, sut);

var reply = await testClient.SendActivityAsync<IMessageActivity>("hi");
Assert.Equal("Where would you like to travel to?", reply.Text);

...

var bookingResults = (BookingDetails)testClient.DialogTurnResult.Result;
Assert.Equal("New York", bookingResults?.Origin);
Assert.Equal("Seattle", bookingResults?.Destination);
Assert.Equal("2019-06-21", bookingResults?.TravelDate);

Die DialogTurnResult-Eigenschaft kann auch verwendet werden, um Zwischenergebnisse zu untersuchen und zu bestätigen, die von den Schritten eines Wasserfalls zurückgegeben werden.

Analysieren der Testausgabe

In einigen Fällen ist es erforderlich, ein Transkript eines Komponententests zu lesen, um die Testausführung zu analysieren, ohne den Test debuggen zu müssen.

Das Paket Microsoft.Bot.Builder.Testing enthält einen XUnitDialogTestLogger zum Protokollieren der Nachrichten, die vom Dialog empfangen und an die Konsole gesendet werden.

Zur Nutzung dieser Middleware muss Ihr Test einen Konstruktor verfügbar machen, der ein vom XUnit Test Runner bereitgestelltes ITestOutputHelper-Objekt empfängt. Außerdem muss ein XUnitDialogTestLogger erstellt werden, der über den Parameter middlewares an DialogTestClient übergeben wird.

public class BookingDialogTests
{
    private readonly IMiddleware[] _middlewares;

    public BookingDialogTests(ITestOutputHelper output)
        : base(output)
    {
        _middlewares = new[] { new XUnitDialogTestLogger(output) };
    }

    [Fact]
    public async Task SomeBookingDialogTest()
    {
        // Arrange
        var sut = new BookingDialog();
        var testClient = new DialogTestClient(Channels.Msteams, sut, middlewares: _middlewares);

        ...
    }
}

Hier ist ein Beispiel dafür angegeben, was vom XUnitDialogTestLogger im Ausgabefenster protokolliert wird, wenn dies konfiguriert ist:

Example middleware output from XUnit.

Weitere Informationen zum Senden von Testausgaben an die Konsole bei Verwendung von XUnit finden Sie in der XUnit-Dokumentation unter Capturing Output (Erfassen der Ausgabe).

Diese Ausgabe wird auch bei Continuous Integration-Builds auf dem Buildserver protokolliert und dient Ihnen als Hilfe beim Analysieren von Buildfehlern.

Datengesteuerte Tests

In den meisten Fällen ändert sich die Dialoglogik nicht, und die unterschiedlichen Ausführungspfade einer Konversation basieren auf den Benutzeräußerungen. Anstatt nur einen Komponententest für jede Variante der Konversation zu schreiben, ist es einfacher, datengesteuerte Tests zu verwenden (auch als parametrisierte Tests bezeichnet).

Der Beispieltest im Übersichtsabschnitt dieses Dokuments zeigt beispielsweise, wie sie einen Ausführungsfluss testen, aber nicht andere, z. B.:

  • Was geschieht, wenn der Benutzer keine Bestätigung angibt?
  • Was geschieht, wenn sie ein anderes Datum verwenden?

Mit datengesteuerten Tests können wir all diese unterschiedlichen Fälle testen, ohne die Tests umschreiben zu müssen.

Im CoreBot-Beispiel verwenden wir Theory-Tests aus XUnit,um die Tests zu parametrisieren.

Theory-Tests mit InlineData

Mit dem folgenden Test wird überprüft, ob ein Dialog abgebrochen wird, wenn der Benutzer „Cancel“ (Abbrechen) sagt.

[Fact]
public async Task ShouldBeAbleToCancel()
{
    var sut = new TestCancelAndHelpDialog();
    var testClient = new DialogTestClient(Channels.Test, sut);

    var reply = await testClient.SendActivityAsync<IMessageActivity>("Hi");
    Assert.Equal("Hi there", reply.Text);
    Assert.Equal(DialogTurnStatus.Waiting, testClient.DialogTurnResult.Status);

    reply = await testClient.SendActivityAsync<IMessageActivity>("cancel");
    Assert.Equal("Cancelling...", reply.Text);
}

Zum Abbrechen eines Dialogs (auf Englisch) können Benutzer „quit“, „never mind“ und „stop it“ eingeben. Sie müssen nicht für jedes mögliche Wort einen neuen Testfall schreiben, sondern lediglich eine Theory-Testmethode, die Parameter aus einer Liste mit InlineData-Werten akzeptiert. Hiermit werden die Parameter für die einzelnen Testfälle definiert:

[Theory]
[InlineData("cancel")]
[InlineData("quit")]
[InlineData("never mind")]
[InlineData("stop it")]
public async Task ShouldBeAbleToCancel(string cancelUtterance)
{
    var sut = new TestCancelAndHelpDialog();
    var testClient = new DialogTestClient(Channels.Test, sut, middlewares: _middlewares);

    var reply = await testClient.SendActivityAsync<IMessageActivity>("Hi");
    Assert.Equal("Hi there", reply.Text);
    Assert.Equal(DialogTurnStatus.Waiting, testClient.DialogTurnResult.Status);

    reply = await testClient.SendActivityAsync<IMessageActivity>(cancelUtterance);
    Assert.Equal("Cancelling...", reply.Text);
}

Der neue Test wird viermal mit den unterschiedlichen Parametern durchgeführt, und jeder Fall wird in Visual Studio-Test-Explorer unter ShouldBeAbleToCancel als untergeordnetes Element angezeigt. Falls Tests wie unten gezeigt nicht erfolgreich sind, können Sie mit der rechten Maustaste klicken und das Fehlerszenario debuggen, anstatt den gesamten Testsatz erneut auszuführen.

Example test results for in-line data.

Theory-Tests mit MemberData und komplexen Typen

InlineData ist für kleinere datengesteuerte Tests hilfreich, bei denen einfache Werttypparameter („string“, „int“ usw.) empfangen werden.

Das BookingDialog-Element empfängt ein BookingDetails-Objekt und gibt ein neues BookingDetails-Objekt zurück. Eine nicht parametrisierte Version eines Tests für diesen Dialog sieht wie folgt aus:

[Fact]
public async Task DialogFlow()
{
    // Initial parameters
    var initialBookingDetails = new BookingDetails
    {
        Origin = "Seattle",
        Destination = null,
        TravelDate = null,
    };

    // Expected booking details
    var expectedBookingDetails = new BookingDetails
    {
        Origin = "Seattle",
        Destination = "New York",
        TravelDate = "2019-06-25",
    };

    var sut = new BookingDialog();
    var testClient = new DialogTestClient(Channels.Test, sut, initialBookingDetails);

    // Act/Assert
    var reply = await testClient.SendActivityAsync<IMessageActivity>("hi");
    ...

    var bookingResults = (BookingDetails)testClient.DialogTurnResult.Result;
    Assert.Equal(expectedBookingDetails.Origin, bookingResults?.Origin);
    Assert.Equal(expectedBookingDetails.Destination, bookingResults?.Destination);
    Assert.Equal(expectedBookingDetails.TravelDate, bookingResults?.TravelDate);
}

Zum Parametrisieren dieses Tests haben wir eine BookingDialogTestCase-Klasse erstellt, die unsere Testfalldaten enthält. Sie enthält das ursprüngliche BookingDetails-Objekt, die erwarteten BookingDetails und ein Array mit Zeichenfolgen, die die vom Benutzer gesendeten Äußerungen und die erwarteten Antworten aus dem Dialog für jeden Turn umfassen.

public class BookingDialogTestCase
{
    public BookingDetails InitialBookingDetails { get; set; }

    public string[,] UtterancesAndReplies { get; set; }

    public BookingDetails ExpectedBookingDetails { get; set; }
}

Wir haben auch die Hilfsklasse BookingDialogTestsDataGenerator erstellt, die eine IEnumerable<object[]> BookingFlows()-Methode verfügbar macht. Hiermit wird eine Sammlung mit den Testfällen zurückgegeben, die vom Test verwendet werden.

Um in Visual Studio-Test-Explorer jeden Testfall als einzelnes Element anzeigen zu können, ist es für den XUnit Test Runner erforderlich, dass für komplexe Typen wie BookingDialogTestCase das IXunitSerializable-Element implementiert wird. Zur Vereinfachung wird über das Bot.Builder.Testing-Framework eine TestDataObject-Klasse bereitgestellt, mit der diese Schnittstelle implementiert wird und die genutzt werden kann, um die Testfalldaten ohne Implementierung von IXunitSerializable zu umschließen.

Hier ist ein Fragment von IEnumerable<object[]> BookingFlows() angegeben, mit dem verdeutlicht wird, wie die beiden Klassen verwendet werden:

public static class BookingDialogTestsDataGenerator
{
    public static IEnumerable<object[]> BookingFlows()
    {
        // Create the first test case object
        var testCaseData = new BookingDialogTestCase
        {
            InitialBookingDetails = new BookingDetails(),
            UtterancesAndReplies = new[,]
            {
                { "hi", "Where would you like to travel to?" },
                { "Seattle", "Where are you traveling from?" },
                { "New York", "When would you like to travel?" },
                { "tomorrow", $"Please confirm, I have you traveling to: Seattle from: New York on: {DateTime.Now.AddDays(1):yyyy-MM-dd}. Is this correct? (1) Yes or (2) No" },
                { "yes", null },
            },
            ExpectedBookingDetails = new BookingDetails
            {
                Destination = "Seattle",
                Origin = "New York",
                TravelDate = $"{DateTime.Now.AddDays(1):yyyy-MM-dd}",
            }, 
        };
        // wrap the test case object into TestDataObject and return it.
        yield return new object[] { new TestDataObject(testCaseData) };

        // Create the second test case object
        testCaseData = new BookingDialogTestCase
        {
            InitialBookingDetails = new BookingDetails
            {
                Destination = "Seattle",
                Origin = "New York",
                TravelDate = null,
            },
            UtterancesAndReplies = new[,]
            {
                { "hi", "When would you like to travel?" },
                { "tomorrow", $"Please confirm, I have you traveling to: Seattle from: New York on: {DateTime.Now.AddDays(1):yyyy-MM-dd}. Is this correct? (1) Yes or (2) No" },
                { "yes", null },
            },
            ExpectedBookingDetails = new BookingDetails
            {
                Destination = "Seattle",
                Origin = "New York",
                TravelDate = $"{DateTime.Now.AddDays(1):yyyy-MM-dd}",
            },
        };
        // wrap the test case object into TestDataObject and return it.
        yield return new object[] { new TestDataObject(testCaseData) };
    }
}

Nachdem wir ein Objekt zum Speichern der Testdaten und eine Klasse erstellt haben, die eine Sammlung mit Testfällen verfügbar macht, verwenden wir das XUnit-Attribut MemberData anstelle von InlineData, um die Daten in den Test einzubinden. Der erste Parameter für MemberData ist der Name der statischen Funktion, mit der die Sammlung mit den Testfällen zurückgegeben wird, und der zweite Parameter ist der Typ der Klasse, mit dem diese Methode verfügbar gemacht wird.

[Theory]
[MemberData(nameof(BookingDialogTestsDataGenerator.BookingFlows), MemberType = typeof(BookingDialogTestsDataGenerator))]
public async Task DialogFlowUseCases(TestDataObject testData)
{
    // Get the test data instance from TestDataObject
    var bookingTestData = testData.GetObject<BookingDialogTestCase>();
    var sut = new BookingDialog();
    var testClient = new DialogTestClient(Channels.Test, sut, bookingTestData.InitialBookingDetails);

    // Iterate over the utterances and replies array.
    for (var i = 0; i < bookingTestData.UtterancesAndReplies.GetLength(0); i++)
    {
        var reply = await testClient.SendActivityAsync<IMessageActivity>(bookingTestData.UtterancesAndReplies[i, 0]);
        Assert.Equal(bookingTestData.UtterancesAndReplies[i, 1], reply?.Text);
    }

    // Assert the resulting BookingDetails object
    var bookingResults = (BookingDetails)testClient.DialogTurnResult.Result;
    Assert.Equal(bookingTestData.ExpectedBookingDetails?.Origin, bookingResults?.Origin);
    Assert.Equal(bookingTestData.ExpectedBookingDetails?.Destination, bookingResults?.Destination);
    Assert.Equal(bookingTestData.ExpectedBookingDetails?.TravelDate, bookingResults?.TravelDate);
}

Hier ist ein Beispiel für die Ergebnisse der DialogFlowUseCases-Tests in Visual Studio-Test-Explorer nach der Ausführung angegeben:

Example results for the booking dialog.

Verwenden von Pseudoelementen

Für die Bereiche, die derzeit nicht getestet werden sollen, können Sie Pseudoelemente verwenden. Zu Referenzzwecken kann diese Ebene grundsätzlich als Einheiten- und Integrationstest betrachtet werden.

Wenn Sie so viele Elemente wie möglich modellieren, erhalten Sie eine bessere Isolierung des zu testenden Elements. Zu den Kandidaten für Modellelemente gehören Speicher, Adapter, Middleware, Aktivitätspipeline, Kanäle und alle weitere Elemente, die nicht direkt zu Ihrem Bot gehören. Dies kann auch das vorübergehende Entfernen bestimmter Aspekte umfassen, um jedes Element zu isolieren. Dies kann beispielsweise Middleware sein, die nicht zu den zu testenden Bereichen Ihres Bots gehört. Wenn Sie Ihre Middleware testen, sollten Sie jedoch möglicherweise stattdessen Ihren Bot modellieren.

Das Modellieren von Elementen kann eine Reihe von Formen annehmen, vom Ersetzen eines Elements durch ein anderes bekanntes Objekt bis hin zum Implementieren einer minimalen Hallo-Welt-Funktionalität. Dies könnte auch in der Form geschehen, dass das Element entfernt wird, wenn es nicht notwendig ist, oder dass es gezwungen wird, nichts zu tun.

Mit Pseudoelementen können wir die Abhängigkeiten eines Dialogs konfigurieren und sicherstellen, dass diese sich während der Durchführung des Tests in einem bekannten Zustand befinden, ohne externe Ressourcen wie Datenbanken, Sprachmodelle oder andere Objekte verwenden zu müssen.

Um für Ihren Dialog das Testen zu vereinfachen und dessen Abhängigkeiten von externen Objekten zu reduzieren, müssen Sie unter Umständen externe Abhängigkeiten in den Konstruktor des Dialogs einfügen.

Beispiel: Anstelle der Instanziierung von BookingDialog in MainDialog:

public MainDialog()
    : base(nameof(MainDialog))
{
    ...
    AddDialog(new BookingDialog());
    ...
}

Übergeben wir eine Instanz von BookingDialog als Konstruktorparameter:

public MainDialog(BookingDialog bookingDialog)
    : base(nameof(MainDialog))
{
    ...
    AddDialog(bookingDialog);
    ...
}

Wir können dann die BookingDialog-Instanz durch ein Pseudoobjekt ersetzen und Komponententests für MainDialog schreiben, ohne die eigentliche BookingDialog-Klasse aufzurufen.

// Create the mock object
var mockDialog = new Mock<BookingDialog>();

// Use the mock object to instantiate MainDialog
var sut = new MainDialog(mockDialog.Object);

var testClient = new DialogTestClient(Channels.Test, sut);

Verwenden von Pseudoelementen für Dialoge

Wie oben beschrieben wird von MainDialog das BookingDialog-Element aufgerufen, um das BookingDetails-Objekt zu erhalten. Wir implementieren und konfigurieren wie folgt eine Pseudoinstanz von BookingDialog:

// Create the mock object for BookingDialog.
var mockDialog = new Mock<BookingDialog>();
mockDialog
    .Setup(x => x.BeginDialogAsync(It.IsAny<DialogContext>(), It.IsAny<object>(), It.IsAny<CancellationToken>()))
    .Returns(async (DialogContext dialogContext, object options, CancellationToken cancellationToken) =>
    {
        // Send a generic activity so we can assert that the dialog was invoked.
        await dialogContext.Context.SendActivityAsync($"{mockDialogNameTypeName} mock invoked", cancellationToken: cancellationToken);

        // Create the BookingDetails instance we want the mock object to return.
        var expectedBookingDialogResult = new BookingDetails()
        {
            Destination = "Seattle",
            Origin = "New York",
            TravelDate = $"{DateTime.UtcNow.AddDays(1):yyyy-MM-dd}"
        };

        // Return the BookingDetails we need without executing the dialog logic.
        return await dialogContext.EndDialogAsync(expectedBookingDialogResult, cancellationToken);
    });

// Create the sut (System Under Test) using the mock booking dialog.
var sut = new MainDialog(mockDialog.Object);

In diesem Beispiel haben wir Moq verwendet, um den Pseudodialog zu erstellen, und die Methoden Setup und Returns, um das Verhalten zu konfigurieren.

Verwenden von LUIS-Pseudoergebnissen

Hinweis

Language Understanding (LUIS) wird am 1. Oktober 2025 eingestellt. Ab dem 1. April 2023 können Sie keine neuen LUIS-Ressourcen erstellen. Eine neuere Version von Language Understanding ist jetzt als Teil von Azure KI Language verfügbar.

Conversational Language Understanding (CLU), ein Feature von Azure KI Language, ist die aktualisierte Version von LUIS. Weitere Informationen zur Unterstützung von Language Understanding im Bot Framework SDK finden Sie unter Natürliches Sprachverständnis.

In einfachen Szenarien können Sie LUIS-Pseudoergebnisse per Code wie folgt implementieren:

var mockRecognizer = new Mock<IRecognizer>();
mockRecognizer
    .Setup(x => x.RecognizeAsync<FlightBooking>(It.IsAny<ITurnContext>(), It.IsAny<CancellationToken>()))
    .Returns(() =>
    {
        var luisResult = new FlightBooking
        {
            Intents = new Dictionary<FlightBooking.Intent, IntentScore>
            {
                { FlightBooking.Intent.BookFlight, new IntentScore() { Score = 1 } },
            },
            Entities = new FlightBooking._Entities(),
        };
        return Task.FromResult(luisResult);
    });

LUIS-Ergebnisse können komplex sein. Wenn dies der Fall ist, ist es einfacher, das gewünschte Ergebnis in einer JSON-Datei zu erfassen, es Ihrem Projekt als Ressource hinzuzufügen und es zu einem LUIS-Ergebnis zu deserialisieren. Ein Beispiel:

var mockRecognizer = new Mock<IRecognizer>();
mockRecognizer
    .Setup(x => x.RecognizeAsync<FlightBooking>(It.IsAny<ITurnContext>(), It.IsAny<CancellationToken>()))
    .Returns(() =>
    {
        // Deserialize the LUIS result from embedded json file in the TestData folder.
        var bookingResult = GetEmbeddedTestData($"{GetType().Namespace}.TestData.FlightToMadrid.json");

        // Return the deserialized LUIS result.
        return Task.FromResult(bookingResult);
    });

Weitere Informationen