Het kernbotvoorbeeld maakt gebruik van Language Understanding (LUIS) om gebruikersintenties te identificeren; Het identificeren van gebruikersintentie is echter niet de focus van dit artikel.
Zie Voor informatie over het identificeren van gebruikersintenties natuurlijke taal en het toevoegen van natuurlijke taal aan uw bot.
Notitie
Language Understanding (LUIS) wordt op 1 oktober 2025 buiten gebruik gesteld.
Vanaf 1 april 2023 kunt u geen nieuwe LUIS-resources maken.
Er is nu een nieuwere versie van taalkennis beschikbaar als onderdeel van Azure AI Language.
Conversational Language Understanding (CLU), een functie van Azure AI Language, is de bijgewerkte versie van LUIS.
Zie Natuurlijke taalkennis voor meer informatie over ondersteuning voor taalkennis in de Bot Framework SDK.
Dialoogvensters testen
In het CoreBot-voorbeeld worden dialoogvensters getest via de DialogTestClient klasse, die een mechanisme biedt voor het testen ervan in isolatie buiten een bot en zonder dat u uw code hoeft te implementeren in een webservice.
Met deze klasse kunt u eenheidstests schrijven waarmee dialoogvensters worden gevalideerd op basis van een turn-by-turn-basis. Eenheidstests met behulp van DialogTestClient klasse moeten werken met andere dialoogvensters die zijn gebouwd met behulp van de botbuilder-dialoogvensterbibliotheek.
In het volgende voorbeeld ziet u tests die zijn afgeleid van 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);
const sut = new BookingDialog();
const testClient = new DialogTestClient('msteams', sut);
let reply = await testClient.sendActivity('hi');
assert.strictEqual(reply.text, 'Where would you like to travel to?');
reply = await testClient.sendActivity('Seattle');
assert.strictEqual(reply.text, 'Where are you traveling from?');
reply = await testClient.sendActivity('New York');
assert.strictEqual(reply.text, 'When would you like to travel?');
reply = await testClient.sendActivity('tomorrow');
assert.strictEqual(reply.text, 'OK, I will book a flight from Seattle to New York for tomorrow, Is this Correct?');
reply = await testClient.sendActivity('yes');
assert.strictEqual(reply.text, 'Sure thing, wait while I finalize your reservation...');
reply = testClient.getNextReply();
assert.strictEqual(reply.text, 'All set, I have booked your flight to Seattle for tomorrow');
De eerste parameter DialogTestClient is het doelkanaal. Hiermee kunt u verschillende renderinglogica testen op basis van het doelkanaal voor uw bot (Teams, Slack enzovoort). Als u twijfelt over uw doelkanaal, kunt u de Emulator id's of Test kanaal-id's gebruiken, maar houd er rekening mee dat sommige onderdelen zich mogelijk anders gedragen, afhankelijk van het huidige kanaal, ConfirmPrompt bijvoorbeeld de ja/nee-opties voor de Test en Emulator kanalen anders weergeven. U kunt deze parameter ook gebruiken om voorwaardelijke renderinglogica in uw dialoogvenster te testen op basis van de kanaal-id.
De tweede parameter is een exemplaar van het dialoogvenster dat wordt getest. In de voorbeeldcode in dit artikel sut vertegenwoordigt u het systeem dat wordt getest.
De DialogTestClient constructor biedt aanvullende parameters waarmee u het clientgedrag verder kunt aanpassen of parameters kunt doorgeven aan het dialoogvenster dat indien nodig wordt getest. U kunt initialisatiegegevens doorgeven voor het dialoogvenster, aangepaste middleware toevoegen of uw eigen TestAdapter en ConversationState exemplaar gebruiken.
Met de SendActivityAsync<IActivity> methode kunt u een tekstuiting of een IActivity tekstuiting naar het dialoogvenster verzenden en het eerste bericht retourneren dat het ontvangt. De <T> parameter wordt gebruikt om een sterk getypt exemplaar van het antwoord te retourneren, zodat u deze kunt bevestigen zonder dat u deze hoeft te casten.
var reply = await testClient.SendActivityAsync<IMessageActivity>("hi");
Assert.Equal("Where would you like to travel to?", reply.Text);
In sommige scenario's kan uw bot verschillende berichten verzenden als reactie op één activiteit, in deze gevallen DialogTestClient worden de antwoorden in de wachtrij geplaatst en kunt u de GetNextReply<IActivity> methode gebruiken om het volgende bericht uit de antwoordwachtrij te plaatsen.
reply = testClient.GetNextReply<IMessageActivity>();
Assert.Equal("All set, I have booked your flight to Seattle for tomorrow", reply.Text);
GetNextReply<IActivity> retourneert null als er geen verdere berichten in de antwoordwachtrij staan.
Met de sendActivity methode kunt u een tekstuiting of een Activity tekstuiting naar het dialoogvenster verzenden en het eerste bericht retourneren dat het ontvangt.
let reply = await testClient.sendActivity('hi');
assert.strictEqual(reply.text, 'Where would you like to travel to?');
In sommige scenario's kan uw bot verschillende berichten verzenden als reactie op één activiteit, in deze gevallen DialogTestClient worden de antwoorden in de wachtrij geplaatst en kunt u de getNextReply methode gebruiken om het volgende bericht uit de antwoordwachtrij te plaatsen.
reply = testClient.getNextReply();
assert.strictEqual(reply.text, 'All set, I have booked your flight to Seattle for tomorrow');
getNextReply retourneert null als er geen verdere berichten in de antwoordwachtrij staan.
De code in het CoreBot-voorbeeld bevestigt alleen de Text eigenschap van de geretourneerde activiteiten. In complexere bots wilt u mogelijk andere eigenschappen, zoals Speak, InputHint, ChannelDataenzovoort, asserteren.
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);
U kunt dit doen door elke eigenschap afzonderlijk te controleren zoals hierboven wordt weergegeven, u kunt uw eigen helperhulpprogramma's schrijven voor het bevestigen van activiteiten of u kunt andere frameworks zoals FluentAssertions gebruiken om aangepaste asserties te schrijven en uw testcode te vereenvoudigen.
De code in het CoreBot-voorbeeld bevestigt alleen de text eigenschap van de geretourneerde activiteiten. In complexere bots wilt u mogelijk andere eigenschappen, zoals speak, inputHint, channelDataenzovoort, asserteren.
assert.strictEqual(reply.text, 'Sure thing, wait while I finalize your reservation...');
assert.strictEqual(reply.speak, 'One moment please...');
assert.strictEqual(reply.inputHint, InputHints.IgnoringInput);
U kunt dit doen door elke eigenschap afzonderlijk te controleren zoals hierboven wordt weergegeven. U kunt uw eigen helperhulpprogramma's schrijven voor het bevestigen van activiteiten of u kunt andere bibliotheken zoals Chai gebruiken om aangepaste asserties te schrijven en uw testcode te vereenvoudigen.
Parameters doorgeven aan uw dialoogvensters
De DialogTestClient constructor heeft een initialDialogOptions constructor die kan worden gebruikt om parameters door te geven aan het dialoogvenster. In dit voorbeeld wordt bijvoorbeeld MainDialog een BookingDetails object geïnitialiseerd op basis van de resultaten van taalherkenning, met de entiteiten die worden omgezet uit de uiting van de gebruiker en wordt dit object doorgegeven in de aanroep om aan te roepen BookingDialog.
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);
BookingDialogontvangt deze parameter en opent deze in de test op dezelfde manier als wanneer deze wordt aangeroepen.MainDialog
const inputDialogParams = {
destination: 'Seattle',
travelDate: formatDate(new Date().setDate(now.getDate() + 1))
};
const sut = new BookingDialog();
const testClient = new DialogTestClient('msteams', sut, inputDialogParams);
BookingDialogontvangt deze parameter en heeft toegang tot deze parameter in de test op dezelfde manier als wanneer deze zou zijn aangeroepen.MainDialog
Sommige dialoogvensters vinden het leuk BookingDialog of DateResolverDialog retourneren een waarde in het dialoogvenster voor aanroepen. Het DialogTestClient object geeft een DialogTurnResult eigenschap weer die kan worden gebruikt om de resultaten te analyseren en te bevestigen die door het dialoogvenster worden geretourneerd.
Bijvoorbeeld:
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);
De DialogTurnResult eigenschap kan ook worden gebruikt om tussenliggende resultaten te inspecteren en te bevestigen die worden geretourneerd door de stappen in een waterval.
Sommige dialoogvensters vinden het leuk BookingDialog of DateResolverDialog retourneren een waarde in het dialoogvenster voor aanroepen. Het DialogTestClient object geeft een dialogTurnResult eigenschap weer die kan worden gebruikt om de resultaten te analyseren en te bevestigen die door het dialoogvenster worden geretourneerd.
Bijvoorbeeld:
const sut = new BookingDialog();
const testClient = new DialogTestClient('msteams', sut);
let reply = await testClient.sendActivity('hi');
assert.strictEqual(reply.text, 'Where would you like to travel to?');
...
const bookingResults = client.dialogTurnResult.result;
assert.strictEqual('New York', bookingResults.destination);
assert.strictEqual('Seattle', bookingResults.origin);
assert.strictEqual('2019-06-21', bookingResults.travelDate);
De dialogTurnResult eigenschap kan ook worden gebruikt om tussenliggende resultaten te inspecteren en te bevestigen die worden geretourneerd door de stappen in een waterval.
Testuitvoer analyseren
Soms is het nodig om een transcriptie van een eenheidstest te lezen om de testuitvoering te analyseren zonder fouten in de test op te sporen.
Het pakket Microsoft.Bot.Builder.Testing bevat een XUnitDialogTestLogger pakket waarin de berichten die door het dialoogvenster worden verzonden en ontvangen, worden in de console opgeslagen.
Als u deze middleware wilt gebruiken, moet uw test een constructor beschikbaar maken die een ITestOutputHelper object ontvangt dat wordt geleverd door de XUnit-testrunner en een XUnitDialogTestLogger object maakt dat wordt doorgegeven DialogTestClient via de middlewares parameter.
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 volgt een voorbeeld van wat de XUnitDialogTestLogger logboeken naar het uitvoervenster zijn geconfigureerd:
Het botbuilder-testing-pakket bevat een DialogTestLogger pakket dat de berichten registreert die door het dialoogvenster naar de console worden verzonden en ontvangen.
Als u deze middleware wilt gebruiken, geeft u deze door aan DialogTestClient de middlewares parameter.
const client = new DialogTestClient('msteams', sut, testData.initialData, [new DialogTestLogger()]);
Hier volgt een voorbeeld van wat de DialogTestLogger logboeken naar het uitvoervenster zijn geconfigureerd:
Deze uitvoer wordt ook geregistreerd op de buildserver tijdens de builds voor continue integratie en helpt u bij het analyseren van buildfouten.
Gegevensgestuurde tests
In de meeste gevallen verandert de dialoogvensterlogica niet en zijn de verschillende uitvoeringspaden in een gesprek gebaseerd op de uitingen van de gebruiker. In plaats van één eenheidstest te schrijven voor elke variant in het gesprek, is het eenvoudiger om gegevensgestuurde tests (ook wel geparameteriseerde test genoemd) te gebruiken.
De voorbeeldtest in de overzichtssectie van dit document laat bijvoorbeeld zien hoe u één uitvoeringsstroom test, maar niet andere, zoals:
Wat gebeurt er als de gebruiker nee op de bevestiging zegt?
Wat gebeurt er als ze een andere datum gebruiken?
Met gegevensgestuurde tests kunnen we al deze permutaties testen zonder de tests opnieuw te hoeven schrijven.
In het CoreBot-voorbeeld gebruiken Theory we tests van XUnit om tests te parameteriseren.
Theorietests met behulp van InlineData
Met de volgende test wordt gecontroleerd of een dialoogvenster wordt geannuleerd wanneer de gebruiker 'annuleren' zegt.
[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);
}
Als u een dialoogvenster wilt annuleren, kunnen gebruikers 'afsluiten', 'nooit erg' en 'stoppen'. In plaats van InlineData een nieuwe testcase te schrijven voor elk mogelijk woord, schrijft u één Theory testmethode die parameters accepteert via een lijst met waarden om de parameters voor elke testcase te definiëren:
[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);
}
De nieuwe test wordt vier keer uitgevoerd met de verschillende parameters en elke case wordt weergegeven als een onderliggend item onder de ShouldBeAbleToCancel test in Visual Studio Test Explorer. Als een van deze mislukt zoals hieronder wordt weergegeven, kunt u met de rechtermuisknop klikken en fouten opsporen in het scenario dat is mislukt in plaats van de volledige set tests opnieuw uit te voeren.
Theorietests met behulp van MemberData en complexe typen
InlineData is handig voor kleine gegevensgestuurde tests die eenvoudige waardetypeparameters (tekenreeks, int enzovoort) ontvangen.
Het BookingDialog ontvangt een BookingDetails object en retourneert een nieuw BookingDetails object. Een niet-geparameteriseerde versie van een test voor dit dialoogvenster ziet er als volgt uit:
[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);
}
Om deze test te parameteriseren, hebben we een BookingDialogTestCase klasse gemaakt die onze testcasegegevens bevat. Het bevat het oorspronkelijke BookingDetails object, de verwachte BookingDetails en een matrix met tekenreeksen die de uitingen bevatten die van de gebruiker zijn verzonden en de verwachte antwoorden uit het dialoogvenster voor elke beurt.
public class BookingDialogTestCase
{
public BookingDetails InitialBookingDetails { get; set; }
public string[,] UtterancesAndReplies { get; set; }
public BookingDetails ExpectedBookingDetails { get; set; }
}
We hebben ook een helperklasse BookingDialogTestsDataGenerator gemaakt waarmee een IEnumerable<object[]> BookingFlows() methode wordt weergegeven die een verzameling testcases retourneert die door de test moeten worden gebruikt.
Om elke testcase weer te geven als een afzonderlijk item in Visual Studio Test Explorer, vereist de XUnit-testloper dat complexe typen, zoals BookingDialogTestCase implementeren IXunitSerializable, om dit te vereenvoudigen, het Bot.Builder.Testing-framework een TestDataObject klasse biedt die deze interface implementeert en kan worden gebruikt om de testcasegegevens te verpakken zonder te hoeven implementeren IXunitSerializable.
Hier volgt een fragment waarin IEnumerable<object[]> BookingFlows() wordt getoond hoe de twee klassen worden gebruikt:
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) };
}
}
Zodra we een object maken voor het opslaan van de testgegevens en een klasse die een verzameling testcases beschikbaar maakt, gebruiken we het kenmerk XUnit MemberData in plaats van InlineData de gegevens in de test in te voeren. De eerste parameter hiervoor MemberData is de naam van de statische functie die de verzameling testcases retourneert en de tweede parameter is het type van de klasse die deze methode beschikbaar maakt.
[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 volgt een voorbeeld van de resultaten voor de DialogFlowUseCases tests in Visual Studio Test Explorer wanneer de test wordt uitgevoerd:
Eenvoudige gegevensgestuurde tests
Met de volgende test wordt gecontroleerd of een dialoogvenster wordt geannuleerd wanneer de gebruiker 'annuleren' zegt.
describe('ShouldBeAbleToCancel', () => {
it('Should cancel', async () => {
const sut = new TestCancelAndHelpDialog();
const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]);
// Execute the test case
let reply = await client.sendActivity('Hi');
assert.strictEqual(reply.text, 'Hi there');
assert.strictEqual(client.dialogTurnResult.status, 'waiting');
reply = await client.sendActivity('cancel');
assert.strictEqual(reply.text, 'Cancelling...');
});
});
Houd er rekening mee dat we later andere utterances moeten kunnen afhandelen voor annuleren, zoals 'afsluiten', 'nooit erg' en 'stoppen'. In plaats van drie terugkerende tests te schrijven voor elke nieuwe uiting, kunnen we de test herstructureren om een lijst met uitingen te gebruiken om de parameters voor elke testcase te definiëren:
describe('ShouldBeAbleToCancel', () => {
const testCases = ['cancel', 'quit', 'never mind', 'stop it'];
testCases.map(testData => {
it(testData, async () => {
const sut = new TestCancelAndHelpDialog();
const client = new DialogTestClient('test', sut, null, [new DialogTestLogger()]);
// Execute the test case
let reply = await client.sendActivity('Hi');
assert.strictEqual(reply.text, 'Hi there');
assert.strictEqual(client.dialogTurnResult.status, 'waiting');
reply = await client.sendActivity(testData);
assert.strictEqual(reply.text, 'Cancelling...');
});
});
});
De nieuwe test wordt vier keer uitgevoerd met de verschillende parameters en elke case wordt weergegeven als een onderliggend item onder de ShouldBeAbleToCancel testsuite in Mocha Test Explorer. Als een van deze mislukt, zoals hieronder wordt weergegeven, kunt u het scenario uitvoeren en fouten opsporen dat is mislukt in plaats van de volledige set tests opnieuw uit te voeren.
Gegevensgestuurde tests met complexe typen
Het gebruik van een eenvoudige lijst met utterances is handig voor kleine gegevensgestuurde tests die eenvoudige waardetypeparameters (tekenreeks, int, enzovoort) of kleine objecten ontvangen.
Het BookingDialog ontvangt een BookingDetails object en retourneert een nieuw BookingDetails object. Een niet-geparameteriseerde versie van een test voor dit dialoogvenster ziet er als volgt uit:
Om deze test te parameteriseren, hebben we een bookingDialogTestCases module gemaakt die de testcasegegevens retourneert. Elk item bevat een testcasenaam, het eerste object 'BookingDetails', de verwachte 'BookingDetails' en een matrix met tekenreeksen met de uitingen die zijn verzonden van de gebruiker en de verwachte antwoorden uit het dialoogvenster voor elke beurt.
module.exports = [
// Create the first test case object
{
name: 'Full flow',
initialData: {},
steps: [
['hi', 'To what city would you like to travel?'],
['Seattle', 'From what city will you be travelling?'],
['New York', 'On what date would you like to travel?'],
['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`],
['yes', null]
],
expectedResult: {
destination: 'Seattle',
origin: 'New York',
travelDate: tomorrow
}
},
// Create the second test case object
{
name: 'Destination and Origin provided',
initialData: {
destination: 'Seattle',
origin: 'New York'
},
steps: [
['hi', 'On what date would you like to travel?'],
['tomorrow', `Please confirm, I have you traveling to: Seattle from: New York on: ${ tomorrow }. Is this correct? (1) Yes or (2) No`],
['yes', null]
],
expectedStatus: 'complete',
expectedResult: {
destination: 'Seattle',
origin: 'New York',
travelDate: tomorrow
}
}
];
Zodra we de lijst met de testgegevens hebben gemaakt, kunnen we onze test herstructureren om deze lijst toe te wijzen aan afzonderlijke testcases.
describe('DialogFlowUseCases', () => {
const testCases = require('./testData/bookingDialogTestCases.js');
testCases.map(testData => {
it(testData.name, async () => {
const sut = new BookingDialog('bookingDialog');
const client = new DialogTestClient('test', sut, testData.initialData, [new DialogTestLogger()]);
// Execute the test case
for (let i = 0; i < testData.steps.length; i++) {
const reply = await client.sendActivity(testData.steps[i][0]);
assert.strictEqual((reply ? reply.text : null), testData.steps[i][1]);
}
// Check dialog results
const actualResult = client.dialogTurnResult.result;
assert.strictEqual(actualResult.destination, testData.expectedResult.destination);
assert.strictEqual(actualResult.origin, testData.expectedResult.origin);
assert.strictEqual(actualResult.travelDate, testData.expectedResult.travelDate);
});
});
});
Hier volgt een voorbeeld van de resultaten voor het DialogFlowUseCases testpakket in Mocha Test Explorer wanneer de testsuite wordt uitgevoerd:
Mocks gebruiken
U kunt mock-elementen gebruiken voor de dingen die momenteel niet zijn getest. Ter referentie kan dit niveau over het algemeen worden beschouwd als eenheids- en integratietests.
Door zoveel elementen te bespotten als u kunt, kunt u het stuk dat u test beter isoleren. Kandidaten voor mock-elementen zijn opslag, de adapter, middleware, activiteitspijplijn, kanalen en alles wat niet rechtstreeks deel uitmaakt van uw bot. Dit kan ook betekenen dat bepaalde aspecten tijdelijk worden verwijderd, zoals middleware die niet betrokken is bij het deel van uw bot dat u test, om elk stuk te isoleren. Als u echter uw middleware test, kunt u in plaats daarvan uw bot mocken.
Mocking-elementen kunnen enkele vormen aannemen, van het vervangen van een element door een ander bekend object om minimale hallo wereldfunctionaliteit te implementeren. Dit kan ook de vorm aannemen van het verwijderen van het element, als het niet nodig is of het afdwingen om niets te doen.
Met mocks kunnen we de afhankelijkheden van een dialoogvenster configureren en ervoor zorgen dat ze zich tijdens de uitvoering van de test in een bekende status bevinden zonder dat we hoeven te vertrouwen op externe resources, zoals databases, taalmodellen of andere objecten.
Als u het dialoogvenster gemakkelijker wilt testen en de afhankelijkheden van externe objecten wilt verminderen, moet u mogelijk de externe afhankelijkheden in de dialoogvensterconstructor injecteren.
Bijvoorbeeld, in plaats van instantiëren BookingDialog in MainDialog:
Hierdoor kunnen we het BookingDialog exemplaar vervangen door een mock-object en eenheidstests schrijven voor MainDialog zonder de werkelijke BookingDialog klasse aan te roepen.
// 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);
// Create the mock object
const mockDialog = new MockBookingDialog();
// Use the mock object to instantiate MainDialog
const sut = new MainDialog(mockDialog);
const testClient = new DialogTestClient('test', sut);
Dialoogvensters met mocking
Zoals hierboven beschreven, MainDialog wordt aangeroepen BookingDialog om het BookingDetails object te verkrijgen. We implementeren en configureren als volgt een mock-exemplaar 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 dit voorbeeld hebben we Moq gebruikt om het mockdialoogvenster en de Setup methoden Returns te maken om het gedrag ervan te configureren.
class MockBookingDialog extends BookingDialog {
constructor() {
super('bookingDialog');
}
async beginDialog(dc, options) {
// Send a generic activity so we can assert that the dialog was invoked.
await dc.context.sendActivity(`${ this.id } mock invoked`);
// Create the BookingDetails instance we want the mock object to return.
const bookingDetails = {
origin: 'New York',
destination: 'Seattle',
travelDate: '2025-07-08'
};
// Return the BookingDetails we need without executing the dialog logic.
return await dc.endDialog(bookingDetails);
}
}
...
// Create the sut (System Under Test) using the mock booking dialog.
const sut = new MainDialog(new MockBookingDialog());
...
In dit voorbeeld implementeren we het voorbeelddialoogvenster door de methode te afleiden BookingDialog en over teschrijven beginDialog om de onderliggende dialoogvensterlogica te omzeilen.
Luis-resultaten nasimuleerden
Notitie
Language Understanding (LUIS) wordt op 1 oktober 2025 buiten gebruik gesteld.
Vanaf 1 april 2023 kunt u geen nieuwe LUIS-resources maken.
Er is nu een nieuwere versie van taalkennis beschikbaar als onderdeel van Azure AI Language.
Conversational Language Understanding (CLU), een functie van Azure AI Language, is de bijgewerkte versie van LUIS.
Zie Natuurlijke taalkennis voor meer informatie over ondersteuning voor taalkennis in de Bot Framework SDK.
In eenvoudige scenario's kunt u mock LUIS-resultaten als volgt implementeren via code:
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);
});
// Create a mock class for the recognizer that overrides executeLuisQuery.
class MockFlightBookingRecognizer extends FlightBookingRecognizer {
constructor(mockResult) {
super();
this.mockResult = mockResult;
}
async executeLuisQuery(context) {
return this.mockResult;
}
}
...
// Create a mock result from a string
const mockLuisResult = JSON.parse(`{"intents": {"BookFlight": {"score": 1}}, "entities": {"$instance": {}}}`);
// Use the mock result with the mock recognizer.
const mockRecognizer = new MockFlightBookingRecognizer(mockLuisResult);
...
LUIS-resultaten kunnen complex zijn. Wanneer dat het is, is het eenvoudiger om het gewenste resultaat vast te leggen in een JSON-bestand, het toe te voegen als een resource aan uw project en het te deserialiseren in een LUIS-resultaat. Hier volgt een voorbeeld:
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);
});
// Create a mock result from a json file
const mockLuisResult = require(`./testData/FlightToMadrid.json`);
// Use the mock result with the mock recognizer.
const mockRecognizer = new MockFlightBookingRecognizer(mockLuisResult);
In deze module leert u hoe u Playwright gebruikt om een voorbeeldwebtoepassing te testen. U leert hoe u tests uitvoert, testrapporten bekijkt en de structuur van een Playwright-project begrijpt. U leert ook hoe u Visual Studio Code gebruikt voor het uitvoeren van tests, foutopsporingstests en het vastleggen van nieuwe tests. Ten slotte leert u hoe u een nieuwe testsuite maakt en hoe u uw tests verfijnt.
Meer informatie over het gebruik van Bot Framework Emulator voor het opsporen van fouten in bots. Lees hoe u onderbrekingspunten instelt in IDE's en hoe u berichten kunt uitwisselen met bots tijdens foutopsporing.
Bekijk tips voor foutopsporing van bots, zoals het gebruik van de emulator en transcripties om gedrag te inspecteren. Inzicht in mogelijke middleware-, status- en activiteitshandlerfouten.
Meer informatie over het gebruik van transcriptiebestanden om fouten in bots op te sporen. Bekijk hoe u deze bestanden maakt en ophaalt, die gedetailleerde sets gebruikersinteracties en botreacties bieden.