This bot will help you to test your APIs by giving the response as passed or failed. In the first part we will cover Open GET APIs. Open GET APIs don’t have any authorization as such. We just provide the URL and call the API, which in turn gives a response along with status code.
Pre-requisites
- Basic knowledge of APIs and its status code.
- Visual Studio
- Bot Framework v4 Templates
- Bot Framework Emulator
Link for last three requirement can be found in Downloads page.
Video
APIs used in this part
You are free to use any other API.
Creating Core/Basic Bot in Visual Studio using Template
Please follow the video to create a bot using the template. After creation, we will edit the code based on our requirement. First 3 minutes shows about to do it. If you want to learn how the Flight Booking bot works, you can watch the complete video.
Just give the project name as “APITesterBot“.
Editing the Project Structure
Remove the following files and folder which are not required for our requirement –
- BookingDetails.cs
- FlightBookingRecognizer.cs
- BookingDialog.cs
- CancelAndHelpDialog.cs
- DateResolverDialog.cs
- CognitiveModels Folder
Remove the following lines of code from Startup.cs
// Register LUIS recognizer
services.AddSingleton<FlightBookingRecognizer>();
// Register the BookingDialog.
services.AddSingleton<BookingDialog>();
Remove all the method content from all methods in MainDialog.cs. Also remove the imports which are showing error. Ignore the error for methods where return statements are missing. Below is the MainDialog class will look like after above removal.
public class MainDialog : ComponentDialog
{
protected readonly ILogger Logger;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
Logger = logger;
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
}
}
You can make use of GitHub code where I have edited the structure.
Adding new class files and folders
I will be using the namespace “APITesterBot” for all the posts in the series.
- Create a new class files “UserDetails.cs” and “APITesterDetails.cs” under APITesterBot namespace.
- Create a new folder “Utilities” under APITesterBot namespace.
- Create a new class file “APITesterRepository.cs” under APITesterBot.Utilities namespace.
- Create a new folder “ExistingAPI” under APITesterBot.Dialogs namespace.
- Create a new class file “ExistingAPIDialog.cs” under APITesterBot.Dialogs.ExistingAPI namespace.
We have now created the structure we will use for this demo. The end structure will look like this –

Adding Testing APIs functionalities to the Bot
Now we are good to go to our main requirement. Open the file welcomeCard.json under APITesterBot.Cards namespace and replace the content with following to create a new Adaptive card that will show up when the user joins the conversation:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.1",
"body": [
{
"type": "Image",
"url": "https://i2.wp.com/jd-bots.com/wp-content/uploads/2020/06/cropped-logo-1.png?fit=653%2C163&ssl=1",
"size": "Stretch"
},
{
"type": "TextBlock",
"spacing": "Medium",
"size": "Large",
"weight": "Bolder",
"text": "Welcome to API Testing Bot",
"wrap": true,
"maxLines": 0,
"horizontalAlignment": "Center"
},
{
"type": "TextBlock",
"text": "This bot will help you to test your APIs by giving the response as passed or failed. In the first part we will cover Open GET APIs. Open GET APIs don't have any authorization as such. We just provide the URL and call the API, which in turn gives a response along with status code.",
"wrap": true
},
{
"type": "TextBlock",
"text": "Now that you have successfully run your bot, test APIs from Existing List or give your own APIs.",
"wrap": true
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Get an overview",
"url": "https://youtu.be/nonUId-ByEI"
},
{
"type": "Action.OpenUrl",
"title": "Learn to Create",
"url": "https://jd-bots.com/2020/12/12/api-testing-bot-part-1-microsoft-bot-framework/"
},
{
"type": "Action.OpenUrl",
"title": "Learn how to deploy",
"url": "https://youtu.be/Lis4AIX8DWw"
}
]
}
This is how your welcome message looks like. The welcome message comes in Adaptive Card. Make your own customized adaptive cards easily here with drag and drop functionality.
If you want to learn creating attractive adaptive cards for your bot, refer my post on “Create Adaptive Cards“.

Replace the code in MainDialog.cs with the below code.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// Generated with Bot Builder V4 SDK Template for Visual Studio CoreBot v4.10.3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdaptiveCards;
using APITesterBot.Dialogs.ExistingAPI;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Logging;
using Microsoft.Recognizers.Text.DataTypes.TimexExpression;
using Newtonsoft.Json.Linq;
namespace APITesterBot.Dialogs
{
public class MainDialog : ComponentDialog
{
protected readonly ILogger Logger;
// Dependency injection uses this constructor to instantiate MainDialog
public MainDialog(ILogger<MainDialog> logger)
: base(nameof(MainDialog))
{
Logger = logger;
AddDialog(new ExistingAPIDialog());
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
IntroStepAsync,
ActStepAsync,
FinalStepAsync,
}));
// The initial child Dialog to run.
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> IntroStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("How would you like to test the APIs?"), cancellationToken);
List<string> operationList = new List<string> { "Existing APIs from List", "Give APIs" };
// Create card
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
{
// Use LINQ to turn the choices into submit actions
Actions = operationList.Select(choice => new AdaptiveSubmitAction
{
Title = choice,
Data = choice, // This will be a string
}).ToList<AdaptiveAction>(),
};
// Prompt
return await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions
{
Prompt = (Activity)MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
// Convert the AdaptiveCard to a JObject
Content = JObject.FromObject(card),
}),
Choices = ChoiceFactory.ToChoices(operationList),
// Don't render the choices outside the card
Style = ListStyle.None,
},
cancellationToken);
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["Operation"] = ((FoundChoice)stepContext.Result).Value;
string operation = (string)stepContext.Values["Operation"];
if (operation.Equals("Existing APIs from List"))
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("You selected Existing APIs from the list."), cancellationToken);
return await stepContext.BeginDialogAsync(nameof(ExistingAPIDialog), new UserDetails(), cancellationToken);
}
else
{
}
return await stepContext.NextAsync(null, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
// Restart the main dialog with a different message the second time around
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
}
}
Install the nuget package “AdaptiveCards”. Right click on dependencies -> Manage Nuget Package -> Search AdaptiveCards in Browse.

Add following code in ExistingAPIDialog.cs.
using AdaptiveCards;
using APITesterBot.Utilities;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Schema;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace APITesterBot.Dialogs.ExistingAPI
{
public class ExistingAPIDialog : ComponentDialog
{
public ExistingAPIDialog() : base(nameof(ExistingAPIDialog))
{
AddDialog(new TextPrompt(nameof(TextPrompt)));
AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt)));
AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
{
RequestTypeStepAsync,
ActStepAsync,
ConfirmStepAsync,
FinalStepAsync,
}));
InitialDialogId = nameof(WaterfallDialog);
}
private async Task<DialogTurnResult> RequestTypeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
await stepContext.Context.SendActivityAsync(
MessageFactory.Text("Please select the Request Type."), cancellationToken);
List<string> operationList = new List<string> { "Get", "Post", "Patch", "Put", "Delete" };
// Create card
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
{
// Use LINQ to turn the choices into submit actions
Actions = operationList.Select(choice => new AdaptiveSubmitAction
{
Title = choice,
Data = choice, // This will be a string
}).ToList<AdaptiveAction>(),
};
// Prompt
return await stepContext.PromptAsync(nameof(ChoicePrompt), new PromptOptions
{
Prompt = (Activity)MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
// Convert the AdaptiveCard to a JObject
Content = JObject.FromObject(card),
}),
Choices = ChoiceFactory.ToChoices(operationList),
// Don't render the choices outside the card
Style = ListStyle.None,
},
cancellationToken);
}
private async Task<DialogTurnResult> ActStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["RequestType"] = ((FoundChoice)stepContext.Result).Value;
string requestType = (string)stepContext.Values["RequestType"];
if (requestType.Equals("Get"))
{
int passCount = 0;
int failCount = 0;
for (int i = 0; i < APITesterDetails.GetAPIList.Count; i++)
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Testing API: " + APITesterDetails.GetAPIList[i]), cancellationToken);
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(APITesterDetails.GetAPIList[i]);
client.DefaultRequestHeaders.Accept.Clear();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, client.BaseAddress);
var response = await APITesterRepository.MakeRequestAsync(request, client);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Status: " + response), cancellationToken);
if (response.Equals("True"))
{
passCount++;
}
else
{
failCount++;
}
}
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Total: " + APITesterDetails.GetAPIList.Count + ", Pass: " + passCount + ", Fail: " + failCount), cancellationToken);
}
return await stepContext.NextAsync(null, cancellationToken);
}
private async Task<DialogTurnResult> ConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions
{
Prompt = MessageFactory.Text("Would you like to change Request Type?")
}, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
if ((bool)stepContext.Result)
{
var promptMessage = "What else can I do for you?";
return await stepContext.ReplaceDialogAsync(InitialDialogId, promptMessage, cancellationToken);
}
else
{
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Ok."));
return await stepContext.EndDialogAsync(cancellationToken: cancellationToken);
}
}
}
}
Add below code in APITesterRepository.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace APITesterBot.Utilities
{
public static class APITesterRepository
{
public static async Task<string> MakeRequestAsync(HttpRequestMessage getRequest, HttpClient client)
{
try
{
var response = await client.SendAsync(getRequest).ConfigureAwait(false);
var responseString = string.Empty;
return response.IsSuccessStatusCode.ToString();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return "False";
}
}
}
}
Add below code in APITesterDetails.cs.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace APITesterBot
{
public static class APITesterDetails
{
public static List<string> GetAPIList = new List<string> { "https://opentdb.com/api.php?amount=10", "https://opentdb.com/api_category.php", "https://opentdb.com/api_count_global.php", "https://api.covid19india.org/v4/data.json" };
}
}
This is the file where our API links are saved in a list. You can add your own APIs here.
Keep the Class file UserDetails.cs empty. Make it public class.
Run the project using Cntrl+F5.
Open your bot emulator and give the bot url “http://localhost:3978/api/messages” to connect. Check the port number on the webpage opened on running the project. For some users it can be different (e.g., 3979).
Below is the output you will get when user joins the conversation.

You must be logged in to post a comment.