Reputation: 147
I need to access user & conversation data while running unit tests.
In my bot code, I have implemented user & conversation data following the MS documentation.
While running unit tests for my dialog, I cannot access to the save user data I updated during tests.
Below is my setup (from Virtual Assistant template BotTestBase.cs
) for the bot test code.
var storage = new MemoryStorage();
Services.AddSingleton(new UserState(storage));
Services.AddSingleton(new ConversationState(storage));
Services.AddSingleton(sp =>
{
var userState = sp.GetService<UserState>();
var conversationState = sp.GetService<ConversationState>();
return new BotStateSet(userState, conversationState);
});
Here is my test function:
userState = new UserState(new MemoryStorage());
var subDialog = new SubDialog(userState);
var MainDialog = new MainDialog(subDialog);
var testClient = new DialogTestClient(Channels.Msteams, MainDialog);
// some code here
reply = await testClient.SendActivityAsync<IMessageActivity>("Yes");
Assert.AreEqual("XXXX", ((HeroCard)reply.Attachments[0].Content).Text);
reply = await testClient.SendActivityAsync<IMessageActivity>("No");
Assert.AreEqual("OOOO", ((HeroCard)reply.Attachments[0].Content).Text);
reply = await testClient.SendActivityAsync<IMessageActivity>("Checkout");
Assert.AreEqual("XOXO", ((HeroCard)reply.Attachments[0].Content).Text);
Below is my user data in subDialog. I can update the data by userProfile.xxxx = oooo
:
var userStateProfileAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
var userProfile = await userStateProfileAccessors.GetAsync(dc.Context, () => new UserProfile());
The dialog would enter the subDialog after sending Yes
, and user data get updated after sending No
.
Right after I send Checkout
, user data will be accessed to determine next step. But seems that user data did not get updated, I found null for all properties inside it.
Does anyone know how to access user data for unit test in bot framework?
Update
I created the MRE. I did this by making a virtual assistant template, then I created a dialog CheckoutDialog.cs that assigns a value to my userProfile.DiscountStatus, and checks it in the next step. I created a test for this dialog CheckoutTests.cs, and when I debug the test I can see that my userProfile.DiscountStatus does not persist its state between steps. Here is the git repo.
Upvotes: 3
Views: 552
Reputation: 53
Try adding the below line above var testClient = new DialogTestClient(Channels.Msteams, MainDialog);
AutoSaveStateMiddleware autoSaveStateMiddleware = new AutoSaveStateMiddleware(userState);
Then changing that line to:
var testClient = new DialogTestClient(Channels.Msteams, MainDialog, middlewares: new List<IMiddleware>() { autoSaveStateMiddleware });
I created a simple repo with 2 projects & 3 classes to test this out: Repo with unit test of a dialog which saves & retrieves user state between 2 turns
Looking at the code of DialogTestClient.cs (line 39) in DialogTestClient.cs from Microsoft.Bot.Builder.Testing the client implements AutoSaveStateMiddleware on the ConversationState only.
public DialogTestClient(string channelId, Dialog targetDialog, object initialDialogOptions = null, IEnumerable<IMiddleware> middlewares = null, ConversationState conversationState = null)
{
ConversationState = conversationState ?? new ConversationState(new MemoryStorage());
_testAdapter = new TestAdapter(channelId)
.Use(new AutoSaveStateMiddleware(ConversationState));
AddUserMiddlewares(middlewares);
var dialogState = ConversationState.CreateProperty<DialogState>("DialogState");
_callback = GetDefaultCallback(targetDialog, initialDialogOptions, dialogState);
}
In your situation you are running the dialog only. From what I have seen online saving of state to the storage provider is normally done in the Bot.OnTurnAsync method. You don't have a bot in this case. Adding AutoSaveStateMiddleware on UserState using the provided IMiddleware parameter will cause the UserState to save. Then you should be able to read what you saved on future turns.
Upvotes: 1