How to write a unit test for Bot Framework v4

I’ve previously written a quick post showing a unit test for v4 preview release. I thought I’d update it.

Goal

Unit Test a dialog we’ve written that asks for a user first and second name.

Steps for creating a unit Test

  1. Create a unit test project in your solution, I chose an MS Test project
  2. Using Nuget install Microsoft.Bot.Builder.Dialogs into the MS Test project
  3. Reference your Bot project
  4. Create a unit test, remember to make it async Task
[TestMethod]
public async Task CreatingAGoodContact()
{
    var convoState = new ConversationState(new MemoryStorage());

    var adapter = new TestAdapter()
        .Use(new AutoSaveStateMiddleware(convoState));

    var dialogState = convoState.CreateProperty("dialogState");

    var dialogs = new DialogSet(dialogState);
    dialogs.Add(new CreateContactDialog(null));

    await new TestFlow(adapter, async (turnContext, cancellationToken) =>
    {
        var dc = await dialogs.CreateContextAsync(turnContext, cancellationToken);
        await dc.ContinueDialogAsync(cancellationToken);
        if (!turnContext.Responded)
        {
            await dc.BeginDialogAsync("CreateContactDialog", null, cancellationToken);
        }
    })
    .Send("Say something to start test")
    .AssertReply("What is their first name?")
    .Send("Jane")
    .AssertReply("What is their last name?")
    .Send("Tan")
    .AssertReply($"I have created a contact called Jane Tan")
    .StartTestAsync();
}
Advertisements

Unit Test Flow in Botframework V4

In theory unit testing in Botframework V4 should easy enough as it’s based on .net core. But, as with many things, it might not be that obvious. So here is a quick snippet showing a test for a Waterfall flow;

[TestMethod]
public async Task Waterfall()
{
    var convoState = new ConversationState<Dictionary>(new MemoryStorage());

    var adapter = new TestAdapter()
        .Use(convoState);

    //var dialogState = convoState.CreateProperty("dialogState");
    var dialogs = new DialogSet();
    dialogs.Add("textPrompt", new TextPrompt());

    dialogs.Add("test",
        new WaterfallStep[]
        {
            async (dc, args, next) =>
            {
                    await dc.Context.SendActivity("Welcome! We need to ask a few questions to get started.");
                    await dc.Prompt("textPrompt", "What's your name?");
            },
            async (dc, args, next) =>
            {
                dc.ActiveDialog.State["name"] = args["Value"];
                await dc.Context.SendActivity($"Thanks {args["Value"]}!");
                await dc.End();
            }
        }
    );

    await new TestFlow(adapter, async (turnContext) =>
    {
        var state = turnContext.GetConversationState<Dictionary>();
        var dc = dialogs.CreateContext(turnContext, state);
        await dc.Continue();
        if (!turnContext.Responded)
        {
            await dc.Begin("test");
        }
    })
    .Send("Hello")
    .AssertReply("Welcome! We need to ask a few questions to get started.")
    .AssertReply("What's your name?")
    .Send("Paul")
    .AssertReply("Thanks Paul!")
    .StartTest();
}

A couple of points to note, you have to send a message to start the flow, and make sure you call turnContext.GetConversationState rather than using a local version of the state.

How to Test async and await

This is one of those, “please stop forgetting this dummy” posts aimed at myself. When you want to test an asynchronous method implemented via the Task-based Asynchronous Pattern (TAP) don’t forget to change the default test method;

Default:

[TestMethod]

public void TestAsyncCall() { … }

to

[TestMethod]

public async Task TestAsyncCall() { … }