How to unit test bots (Jak testować jednostkowe boty)

DOTYCZY: ZESTAW SDK w wersji 4

W tym temacie pokażemy, jak wykonać następujące działania:

  • Tworzenie testów jednostkowych dla botów.
  • Użyj asercji, aby sprawdzić działania zwracane przez okno dialogowe z oczekiwanymi wartościami.
  • Użyj potwierdzenia, aby sprawdzić wyniki zwrócone przez okno dialogowe.
  • Utwórz różne typy testów opartych na danych.
  • Utwórz makiety obiektów dla różnych zależności okna dialogowego, takich jak rozpoznawanie języka itd.

Wymagania wstępne

Przykład testów CoreBot używany w tym temacie odwołuje się do pakietu Microsoft.Bot.Builder.Testing , XUnit i Moq w celu utworzenia testów jednostkowych.

Podstawowy przykład bota używa usługi Language Understanding (LUIS) do identyfikowania intencji użytkowników; jednak identyfikowanie intencji użytkownika nie jest celem tego artykułu. Aby uzyskać informacje na temat identyfikowania intencji użytkowników, zobacz Opis języka naturalnego i Dodawanie interpretacji języka naturalnego do bota.

Uwaga

Usługa Language Understanding (LUIS) zostanie wycofana 1 października 2025 r. Od 1 kwietnia 2023 r. nie będzie można tworzyć nowych zasobów usługi LUIS. Nowsza wersja interpretacji języka jest teraz dostępna w ramach języka sztucznej inteligencji platformy Azure.

Język konwersacyjny (CLU), funkcja języka AI platformy Azure, to zaktualizowana wersja usługi LUIS. Aby uzyskać więcej informacji na temat obsługi języka w zestawie SDK platformy Bot Framework, zobacz Opis języka naturalnego.

Okna dialogowe testowania

W przykładzie CoreBot okna dialogowe są testowane jednostką za pośrednictwem DialogTestClient klasy, która zapewnia mechanizm testowania ich w izolacji poza botem i bez konieczności wdrażania kodu w usłudze internetowej.

Korzystając z tej klasy, można pisać testy jednostkowe, które weryfikują odpowiedzi okien dialogowych na zasadzie turn-by-turn basis. Testy jednostkowe używające DialogTestClient klasy powinny współdziałać z innymi oknami dialogowymi utworzonymi przy użyciu biblioteki okien dialogowych botbuilder.

W poniższym przykładzie pokazano testy pochodzące z elementu DialogTestClient:

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);

Klasa jest zdefiniowana DialogTestClient w Microsoft.Bot.Builder.Testing przestrzeni nazw i zawarta w pakiecie NuGet Microsoft.Bot.Builder.Testing .

DialogTestClient

Pierwszy parametr to kanał docelowy DialogTestClient . Dzięki temu można przetestować inną logikę renderowania na podstawie kanału docelowego bota (Teams, Slack itd.). Jeśli nie masz pewności co do kanału docelowego, możesz użyć Emulator identyfikatorów kanałów lub Test , ale pamiętaj, że niektóre składniki mogą zachowywać się inaczej w zależności od bieżącego kanału, na przykład ConfirmPrompt renderuje opcje Tak/Nie dla Test kanałów i Emulator . Możesz również użyć tego parametru, aby przetestować logikę renderowania warunkowego w oknie dialogowym na podstawie identyfikatora kanału.

Drugi parametr to wystąpienie testowanego okna dialogowego. W przykładowym kodzie w tym artykule sut reprezentuje test systemu.

Konstruktor DialogTestClient udostępnia dodatkowe parametry, które umożliwiają dalsze dostosowywanie zachowania klienta lub przekazywanie parametrów do testowanego okna dialogowego w razie potrzeby. Możesz przekazać dane inicjowania dla okna dialogowego, dodać niestandardowe oprogramowanie pośredniczące lub użyć własnego testadapter i ConversationState wystąpienia.

Wysyłanie i odbieranie komunikatów

Metoda SendActivityAsync<IActivity> umożliwia wysłanie wypowiedzi tekstowej lub do IActivity okna dialogowego i zwrócenie pierwszego odbieranego komunikatu. Parametr <T> jest używany do zwracania silnego wystąpienia typu odpowiedzi, aby można było go potwierdzić bez konieczności rzutowania.

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

W niektórych scenariuszach bot może wysyłać kilka komunikatów w odpowiedzi na pojedyncze działanie, w takich przypadkach DialogTestClient kolejkuje odpowiedzi i można użyć GetNextReply<IActivity> metody , aby wyświetlić następny komunikat z kolejki odpowiedzi.

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

GetNextReply<IActivity> funkcja zwróci wartość null, jeśli w kolejce odpowiedzi nie ma żadnych dalszych komunikatów.

Asertywne działania

Kod w przykładzie CoreBot potwierdza Text tylko właściwość zwróconych działań. W bardziej złożonych botach możesz chcieć potwierdzić inne właściwości, takie jak Speak, InputHint, ChannelDatai tak dalej.

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);

Możesz to zrobić, sprawdzając poszczególne właściwości indywidualnie, jak pokazano powyżej, możesz napisać własne narzędzia pomocnicze do asercji działań lub użyć innych struktur, takich jak FluentAssertions , aby napisać niestandardowe asercje i uprościć kod testowy.

Przekazywanie parametrów do okien dialogowych

Konstruktor DialogTestClient ma element initialDialogOptions , który może służyć do przekazywania parametrów do okna dialogowego. Na przykład element MainDialog w tym przykładzie inicjuje BookingDetails obiekt z wyników rozpoznawania języka z jednostkami rozpoznawanymi przez użytkownika i przekazuje ten obiekt w wywołaniu , aby wywołać metodę BookingDialog.

Można to zaimplementować w teście w następujący sposób:

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);

BookingDialog odbiera ten parametr i uzyskuje do niego dostęp w teście w taki sam sposób, jak w przypadku wywołania z metody MainDialog.

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

Okno dialogowe potwierdzenia powoduje przekształcenie wyników

Niektóre okna dialogowe, takie jak BookingDialog lub DateResolverDialog zwracają wartość do okna dialogowego wywoływania. Obiekt DialogTestClient uwidacznia DialogTurnResult właściwość, która może służyć do analizowania i potwierdzania wyników zwracanych przez okno dialogowe.

Przykład:

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);

Właściwość może również służyć do sprawdzania DialogTurnResult i potwierdzania wyników pośrednich zwracanych przez kroki w kaskadzie.

Analizowanie danych wyjściowych testu

Czasami konieczne jest odczytanie transkrypcji testu jednostkowego w celu przeanalizowania wykonania testu bez konieczności debugowania testu.

Pakiet Microsoft.Bot.Builder.Testing zawiera element XUnitDialogTestLogger , który rejestruje komunikaty wysyłane i odbierane przez okno dialogowe do konsoli programu .

Aby użyć tego oprogramowania pośredniczącego, test musi uwidocznić konstruktor, który odbiera ITestOutputHelper obiekt dostarczany przez moduł uruchamiający testy XUnit i utworzyć element XUnitDialogTestLogger , który zostanie przekazany do DialogTestClient parametru middlewares .

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);

        ...
    }
}

Oto przykład tego, co dzienniki w XUnitDialogTestLogger oknie danych wyjściowych są skonfigurowane:

Example middleware output from XUnit.

Aby uzyskać dodatkowe informacje na temat wysyłania danych wyjściowych testów do konsoli podczas korzystania z narzędzia XUnit, zobacz Przechwytywanie danych wyjściowych w dokumentacji narzędzia XUnit.

Te dane wyjściowe będą również rejestrowane na serwerze kompilacji podczas kompilacji ciągłej integracji i ułatwiają analizowanie błędów kompilacji.

Testy oparte na danych

W większości przypadków logika okna dialogowego nie zmienia się, a różne ścieżki wykonywania w konwersacji są oparte na wypowiedziach użytkownika. Zamiast pisać pojedynczy test jednostkowy dla każdego wariantu w konwersacji, łatwiej jest używać testów opartych na danych (nazywanych również testem sparametryzowanym).

Na przykład przykładowy test w sekcji przeglądu tego dokumentu pokazuje, jak przetestować jeden przepływ wykonywania, ale nie inne, takie jak:

  • Co się stanie, jeśli użytkownik nie zostanie wyświetlony na potwierdzenie?
  • Co zrobić, jeśli używają innej daty?

Testy sterowane danymi umożliwiają testowanie wszystkich tych permutacji bez konieczności ponownego pisania testów.

W przykładzie CoreBot używamy Theory testów z narzędzia XUnit do parametryzacji testów.

Testy teorii przy użyciu metody InlineData

Poniższy test sprawdza, czy okno dialogowe zostanie anulowane po wyświetleniu komunikatu "anuluj".

[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);
}

Aby anulować okno dialogowe, użytkownicy mogą wpisać "quit", "never mind" i "stop it". Zamiast pisać nowy przypadek testowy dla każdego możliwego słowa, napisz pojedynczą Theory metodę testową, która akceptuje parametry za pomocą listy InlineData wartości w celu zdefiniowania parametrów dla każdego przypadku testowego:

[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);
}

Nowy test zostanie wykonany cztery razy z różnymi parametrami, a każdy przypadek będzie wyświetlany jako element podrzędny w ShouldBeAbleToCancel ramach testu w Eksploratorze testów programu Visual Studio. Jeśli którykolwiek z nich nie powiedzie się, jak pokazano poniżej, możesz kliknąć prawym przyciskiem myszy i debugować scenariusz, który zakończył się niepowodzeniem, zamiast ponownie uruchamiać cały zestaw testów.

Example test results for in-line data.

Testy teorii przy użyciu atrybutów MemberData i typów złożonych

InlineData jest przydatny w przypadku małych testów opartych na danych, które otrzymują proste parametry typu wartości (ciąg, int itd.).

Obiekt BookingDialog odbiera BookingDetails obiekt i zwraca nowy BookingDetails obiekt. Niesparametryzowana wersja testu dla tego okna dialogowego wygląda następująco:

[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);
}

Aby sparametryzować ten test, utworzyliśmy klasę zawierającą BookingDialogTestCase nasze dane przypadku testowego. Zawiera obiekt początkowy BookingDetails , oczekiwaną BookingDetails i tablicę ciągów zawierających wypowiedzi wysyłane od użytkownika oraz oczekiwane odpowiedzi z okna dialogowego dla każdego kolei.

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

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

    public BookingDetails ExpectedBookingDetails { get; set; }
}

Utworzyliśmy również klasę pomocnika BookingDialogTestsDataGenerator , która uwidacznia metodę IEnumerable<object[]> BookingFlows() zwracającą kolekcję przypadków testowych do użycia przez test.

Aby wyświetlić każdy przypadek testowy jako oddzielny element w Eksploratorze testów programu Visual Studio, moduł uruchamiający testy XUnit wymaga, aby złożone typy, takie jak BookingDialogTestCase implementowanie IXunitSerializable, uprościć ten przypadek, platforma Bot.Builder.Testing udostępnia klasę TestDataObject , która implementuje ten interfejs i może służyć do zawijania danych przypadku testowego bez konieczności implementowania IXunitSerializable.

Oto fragment pokazujący IEnumerable<object[]> BookingFlows() , jak są używane dwie klasy:

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) };
    }
}

Po utworzeniu obiektu do przechowywania danych testowych i klasy, która uwidacznia kolekcję przypadków testowych, używamy atrybutu XUnit MemberData zamiast InlineData do podawania danych do testu, pierwszym parametrem MemberData jest nazwa funkcji statycznej zwracającej kolekcję przypadków testowych, a drugi parametr jest typem klasy, która uwidacznia tę metodę.

[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);
}

Oto przykład wyników testów DialogFlowUseCases w Eksploratorze testów programu Visual Studio po wykonaniu testu:

Example results for the booking dialog.

Korzystanie z makiety

Można użyć pozorowanych elementów dla rzeczy, które nie są obecnie testowane. Dla celów referencyjnych ten poziom można ogólnie traktować jako testy jednostkowe i integracyjne.

Wyśmiewanie jak najwięcej elementów, co pozwala na lepszą izolację elementu, który testujesz. Kandydaci do pozorowania elementów obejmują magazyn, adapter, oprogramowanie pośredniczące, potok działania, kanały i wszystkie inne elementy, które nie są bezpośrednio częścią bota. Może to również obejmować tymczasowe usunięcie niektórych aspektów, takich jak oprogramowanie pośredniczące, które nie jest zaangażowane w część testowanego bota, w celu odizolowania każdego elementu. Jeśli jednak testujesz oprogramowanie pośredniczące, możesz zamiast tego wyśmiewać bota.

Pozorowanie elementów może przyjąć kilka formularzy, zastępując element innym znanym obiektem, aby zaimplementować minimalną funkcjonalność hello world. Może to również mieć formę usuwania elementu, jeśli nie jest to konieczne, lub zmuszanie go do nic.

Makiety pozwalają nam skonfigurować zależności okna dialogowego i upewnić się, że są w znanym stanie podczas wykonywania testu bez konieczności polegania na zasobach zewnętrznych, takich jak bazy danych, modele językowe lub inne obiekty.

Aby ułatwić testowanie i zmniejszanie zależności okna dialogowego od obiektów zewnętrznych, może być konieczne wprowadzenie zależności zewnętrznych w konstruktorze okna dialogowego.

Na przykład zamiast tworzenia wystąpień BookingDialog w pliku MainDialog:

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

Przekazujemy wystąpienie BookingDialog jako parametr konstruktora:

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

Dzięki temu możemy zastąpić BookingDialog wystąpienie pozornym obiektem i napisać testy jednostkowe bez MainDialog konieczności wywoływania rzeczywistej BookingDialog klasy.

// 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);

Pozorowanie okien dialogowych

Jak opisano powyżej, MainDialog wywołuje metodę BookingDialogBookingDetails w celu uzyskania obiektu. Implementujemy i konfigurujemy pozorne wystąpienie programu BookingDialog w następujący sposób:

// 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);

W tym przykładzie użyto narzędzia Moq do utworzenia pozorowego okna dialogowego i Setup metod i Returns w celu skonfigurowania jego zachowania.

Pozorowanie wyników usługi LUIS

Uwaga

Usługa Language Understanding (LUIS) zostanie wycofana 1 października 2025 r. Od 1 kwietnia 2023 r. nie będzie można tworzyć nowych zasobów usługi LUIS. Nowsza wersja interpretacji języka jest teraz dostępna w ramach języka sztucznej inteligencji platformy Azure.

Język konwersacyjny (CLU), funkcja języka AI platformy Azure, to zaktualizowana wersja usługi LUIS. Aby uzyskać więcej informacji na temat obsługi języka w zestawie SDK platformy Bot Framework, zobacz Opis języka naturalnego.

W prostych scenariuszach można zaimplementować pozorne wyniki usługi LUIS za pomocą kodu w następujący sposób:

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);
    });

Wyniki usługi LUIS mogą być złożone. Gdy są, łatwiej jest przechwycić żądany wynik w pliku JSON, dodać go jako zasób do projektu i zdeserializować go w wyniku usługi LUIS. Oto przykład:

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);
    });

Dodatkowe informacje