Sorry for two posts.
I have the following code which I stole from the MahApps example. This has the updated code example
using System.Threading.Tasks;
using MahApps.Metro.Controls.Dialogs;
namespace MyNameSpace {
public class MyClass {
public MyClass(IDialogCoordinator dialogCoordinator,IMyService myservice){
_dialogCoordinator =dialogCoordinator;
_mySerice =myservice;
}
public async Task<bool> MethodUnderTest() {
var controller = await _dialogCoordinator.ShowProgressAsync(this,"Please wait...", "We are baking some cupcakes!");
controller.SetIndeterminate();
await Task.Delay(5000);
controller.SetCancelable(true);
if (!controller.IsCanceled) {
controller.SetProgress(0.50);
controller.SetMessage("Half way there");
await Task.Factory.StartNew(()=> {
_myservice.MyMethodIWannaTest();
});
}
await controller.CloseAsync();
if (controller.IsCanceled) {
await dialogCoordinator.ShowMessageAsync(this,"No cupcakes!", "You stopped baking!");
return false;
} else {
await dialogCoordinator.ShowMessageAsync(this,"Cupcakes!", "Your cupcakes are finished! Enjoy!");
return true;
}
}
private IDialogCoordinator _dialogCoordinator;
private IMyService _myService;
}
}
How do I test to verify that MyMethodIWannaTest is called (Since I can't Moq ShowProgressAync???
using Moq;
using MahApps.Metro.Controls.Dialogs;
[TestMethod]
public void MethodUnderTestCallsMyServiceMyMethodIWannaTest() {
var _mockDialogCoordinator = new Mock<IDialogCoordinator>();
var _mockMyService = new Mock<IMyService>();
// Arrange
var viewModel = new MyClass(_mockDialogCoordinator.Object,_mockMyService.Object);
//NOT SURE WHAT TO DO TO MAKE IT WORK?
_mockDialogCoordinator.Setup(
d => d.ShowProgressAsync(viewModel, "Please wait...", "We are creating your package!", true, null))
.Returns(Task.Factory.StartNew(Activator.CreateInstance<ProgressDialogController>)); //Still does not work always returns null :(
// Act
viewModel.MethodUnderTest();
// Assert
_mockMyService.Verify(ms => ms.MyMethodIWannaTest());
}
Not sure what I am doing wrong, any help appreciated!!
Related
When I run this test, The log "End-Test" is not printed, but when I debug it step by step, it does printed.
What is the reson for this ?
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using NUnit.Framework;
namespace Tests.Integrations
{
[TestFixture]
public class Test
{
private static ConcurrentDictionary<DateTimeOffset, string> _log;
static Test()
{
_log = new ConcurrentDictionary<DateTimeOffset, string>();
}
[Test]
[TestCase("Why_End-Test_is_notPrinted")]
public async Task main(string testId)
{
LogWrite($"START-Test");
var sucssess = await SomethingSlow();
LogWrite($"END-Test, sucssess:{sucssess}");
LogSave(testId);
}
private async Task<bool> SomethingSlow()
{
return await Task<bool>.Run(() =>
{
LogWrite("Something Slow In");
for (int i = 0; i < int.MaxValue; i++) { };
LogWrite("Something Slow Out");
return true;
});
}
public void LogWrite(string msg)
{
Task.Run(() =>
{
var time = DateTimeOffset.UtcNow;
var ts = time
.ToLocalTime()
.ToString("dd/MM/yy HH:mm:ss,fffffff");
var threadId = Thread.CurrentThread.ManagedThreadId;
_log.TryAdd(time, $"{ts}-[{threadId}]: {msg}");
});
}
public void LogSave(string testId)
{
var sorted = LogSort();
string json = JsonConvert.SerializeObject(sorted, Formatting.Indented);
string Path = $"{DirectoryPath.GetDirectoryPath()}";
using (
System.IO.StreamWriter file =
new System.IO.StreamWriter($"{Path}\\ActualResults\\{testId}.json"))
{
file.WriteLine(json);
}
}
public List<string> LogSort()
{
var sortedLog = new List<string>();
var logKeys = _log.Keys.ToList();
logKeys.Sort();
logKeys.ForEach(key =>
{
string value = string.Empty;
if (_log.TryGetValue(key, out value))
sortedLog.Add(value);
});
return sortedLog;
}
}
}
The log output without debugging:
[
"16/08/20 11:27:34,1133986-[9]: START-Test",
"16/08/20 11:27:34,1144001-[11]: Something Slow In",
"16/08/20 11:27:39,3383941-[9]: Something Slow Out"
]
The log output with debugging step by step:
[
"16/08/20 11:29:18,8003608-[9]: START-Test",
"16/08/20 11:29:18,8033588-[10]: Something Slow In",
"16/08/20 11:29:24,1967210-[10]: Something Slow Out",
"16/08/20 11:29:35,0146056-[9]: END-Test, sucssess:True"
]
The expected result is as it is printed in the log while debugging step by step.
Because I expect that when awaiting in the main task to the task "SomethingSlow()", the remaining lines of code in main task shell not be treated untill "SomethingSlow()" returns, and then the log("End-Test") should be handled.
In addition the moving between the threads is not as excpected because both lines "Something Slow In" and "Something Slow Out" are in the same task, so they have to be in the same thread, but in running without debugging it is not like that.
When you run the code without debugging, the task doesn't finish in time. So you must await it.
Cnange LogWrite method:
public Task LogWrite(string msg)
{
return Task.Run(() =>
{
...
});
}
Then await this method:
public async Task main(string testId)
{
await LogWrite($"START-Test");
var sucssess = await SomethingSlow();
await LogWrite($"END-Test, sucssess:{sucssess}");
LogSave(testId);
}
Also simplify SomethingSlow method:
private Task<bool> SomethingSlow()
{
return Task<bool>.Run(() =>
{
...
});
}
I am trying to create a bot wherein based on luis intent i need to ask customer to feed in his details and feedback. For example, if customer is unhappy i want his details to allow a callback. My luis intent identifies the dialog but i am not able to fire up a form. My luis dialog code is
namespace Microsoft.Bot.Sample.Luisbot
{
[Serializable]
public class FeedbackForm
{
[Prompt(new string[] { "Name?" })]
public string Name { get; set; }
[Prompt("Contact Number")]
public string Contact { get; set; }
[Prompt("Query")]
public string Query { get; set; }
public static IForm<FeedbackForm> BuildForm()
{
return new FormBuilder<FeedbackForm>()
.Build();
}
}
[Serializable]
class BasicLuisDialog : LuisDialog<object>
{
public BasicLuisDialog() : base(new LuisService(new LuisModelAttribute(ConfigurationManager.AppSettings["LuisAppId"], ConfigurationManager.AppSettings["LuisAPIKey"])))
[LuisIntent("Greetings")]
public async Task GreetingsIntent(IDialogContext context, LuisResult result)
{
await context.PostAsync("Hi. Please share your query");
context.Wait(MessageReceived);
}
[LuisIntent("Critical")]
public async Task CriticalIntent(IDialogContext context, LuisResult result)
{
await context.PostAsync("Thank you for your response.To help you better I will arrange a call back from our customer care team. Please provide following details");
var feedbackForm = new FormDialog<FeedbackForm>(new FeedbackForm(), FeedbackForm.BuildForm, FormOptions.PromptInStart,result.Entities);
context.Call(feedbackForm, FeedbackFormComplete);
context.Wait(MessageReceived);
}
}
}
And my messagecontroller code is
namespace Microsoft.Bot.Sample.LuisBot
{
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Bot.Connector;
[BotAuthentication]
public class MessagesController : ApiController
{
private static IForm<FeedbackForm> BuildForm()
internal static IDialog<FeedbackForm> MakeRoot()
{
return Chain.From(() => new BasicLuisDialog(BuildForm));
}
[ResponseType(typeof(void))]
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
await Conversation.SendAsync(activity, () => new BasicLuisDialog());
}
else
{
await this.HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private async Task HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
if (message.MembersAdded.Any(o => o.Id == message.Recipient.Id))
{
ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl));
var reply = message.CreateReply();
reply.Text = "Welcome to RB Customer Care";
await client.Conversations.ReplyToActivityAsync(reply);
}
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
}
}
}
Can anyone help me understand what is wrong here. I am not a pro at C#
I have a bot with a root LuisDialog and 4 more LuisDialogs each one with a different LUIS model. Following the conversation started here I've implemented a similar DialogFactory strategy.
When a user sends a question that matches "None" intent in my root dialog, I evaluate the rest of dialogs until I find a match and then forward the message to the "winner".
The problem I'm facing is that I'm getting the http error: 429 (Too Many Requests) when querying LUIS (BaseDialog class).
Any ideas about how to face this?
The "None" intent in my root dialog:
[LuisIntent("None")]
public async Task None(IDialogContext context, IAwaitable<IMessageActivity> message, LuisResult result)
{
var activity = await message;
var factory = new DialogFactory();
BaseDialog<object> dialog = await factory.Create(result.Query);
if (dialog != null)
{
await context.Forward(dialog, EndDialog, activity, CancellationToken.None);
}
else
{
await context.PostAsync("No results!");
}
}
public static async Task EndDialog(IDialogContext context, IAwaitable<object> result)
{
//...
}
The DialogFactory class:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Threading.Tasks;
namespace CodeBot.Dialogs
{
public class DialogFactory
{
private static object _lock = new object();
private static List<BaseDialog<object>> Dialogs { get; set; }
public async Task<BaseDialog<object>> Create(string query)
{
query = query.ToLowerInvariant();
EnsureDialogs();
foreach (var dialog in Dialogs)
{
if (await dialog.CanHandle(query))
{
return dialog;
}
}
return null;
}
private void EnsureDialogs()
{
if (Dialogs == null || (Dialogs.Count != 4))
{
lock (_lock)
{
if (Dialogs == null)
{
Dialogs = new List<BaseDialog<object>>();
}
else if (Dialogs.Count != 4)
{
Dialogs.Clear();
}
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog1));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog2));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog3));
Dialogs.Add((BaseDialog<object>)Activator.CreateInstance(typeof(Dialog4));
}
}
}
}
}
And finally, the BaseDialog class (where I'm getting the error):
using Microsoft.Bot.Builder.Dialogs;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Luis;
using System;
namespace CodeBot.Dialogs
{
[Serializable]
public class BaseDialog<R> : LuisDialog<R>
{
public LuisModelAttribute Luis_Model { get; private set; }
public BaseDialog(LuisModelAttribute luisModel) : base(new LuisService(luisModel))
{
Luis_Model = luisModel;
}
public async Task<bool> CanHandle(string query)
{
try
{
var tasks = services.Select(s => s.QueryAsync(query, CancellationToken.None)).ToArray();
var results = await Task.WhenAll(tasks); <-- Error!!!
var winners = from result in results.Select((value, index) => new { value, index })
let resultWinner = BestIntentFrom(result.value)
where resultWinner != null
select new LuisServiceResult(result.value, resultWinner, this.services[result.index]);
var winner = this.BestResultFrom(winners);
return winner != null && !winner.BestIntent.Intent.Equals(Constants.NONE, StringComparison.InvariantCultureIgnoreCase);
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine($"CanHandle error: {e.Message}");
return false;
}
}
}
}
The 429 error is caused by your application (key) hitting the LUIS API too heavily.
You need to either throttle your requests to ensure you stay below the threshold of the free tier, or upgrade to the Basic plan which allows 50 requests a second.
I'm starting a project for a ChatBot in C# with the bot framework.
I choose the ContosoFlowers example for learning about the use of bot framework. In the AddressDialog users cannot quit the dialog after they enter to it without providing an address.
How can I update the code so when users reply "Cancel" or "Abort" or "B" or "Back" they quit the dialog?
namespace ContosoFlowers.BotAssets.Dialogs
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Extensions;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Connector;
using Properties;
using Services;
[Serializable]
public class AddressDialog : IDialog<string>
{
private readonly string prompt;
private readonly ILocationService locationService;
private string currentAddress;
public AddressDialog(string prompt, ILocationService locationService)
{
this.prompt = prompt;
this.locationService = locationService;
}
public async Task StartAsync(IDialogContext context)
{
await context.PostAsync(this.prompt);
context.Wait(this.MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
var addresses = await this.locationService.ParseAddressAsync(message.Text);
if (addresses.Count() == 0)
{
await context.PostAsync(Resources.AddressDialog_EnterAddressAgain);
context.Wait(this.MessageReceivedAsync);
}
else if (addresses.Count() == 1)
{
this.currentAddress = addresses.First();
PromptDialog.Choice(context, this.AfterAddressChoice, new[] { Resources.AddressDialog_Confirm, Resources.AddressDialog_Edit }, this.currentAddress);
}
else
{
var reply = context.MakeMessage();
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
foreach (var address in addresses)
{
reply.AddHeroCard(Resources.AddressDialog_DidYouMean, address, new[] { new KeyValuePair<string, string>(Resources.AddressDialog_UseThisAddress, address) });
}
await context.PostAsync(reply);
context.Wait(this.MessageReceivedAsync);
}
}
private async Task AfterAddressChoice(IDialogContext context, IAwaitable<string> result)
{
try
{
var choice = await result;
if (choice == Resources.AddressDialog_Edit)
{
await this.StartAsync(context);
}
else
{
context.Done(this.currentAddress);
}
}
catch (TooManyAttemptsException)
{
throw;
}
}
}
}
You just need to handle the quit conditions at the top of the MessageReceivedAsync method.
At the top of the dialog you can add something like
private static IEnumerable<string> cancelTerms = new[] { "Cancel", "Back", "B", "Abort" };
And also add this method:
public static bool IsCancel(string text)
{
return cancelTerms.Any(t => string.Equals(t, text, StringComparison.CurrentCultureIgnoreCase));
}
Then is just a matter of understanding if the message sent by the user matches any of the cancelation terms. In the MessageReceivedAsync method do something like:
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (IsCancel(message.Text))
{
context.Done<string>(null);
}
// rest of the code of this method..
}
You can also go a bit more generic and create a CancelableIDialog similar to what was done in the CancelablePromptChoice.
How can I verify that a method was called on a mock when the method itself is called in a delegate passed to Task.Run? By time mock.Verify is called the Task still hasn't executed.
I have tried await Task.Delay just before mock.Verify but this seems to leave the test runner hanging.
The reason for using Task.Run is to offload the logic to prevent an attacker from being able to differentiate whether the email address exists in the system by the time to execute.
using System.Threading.Tasks;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace AsyncTesting
{
class MyController : Controller
{
public IEmailService EmailService { get; set; }
public MyController(IEmailService emailService)
{
EmailService = emailService;
}
public ViewResult BeginPasswordReset(string emailAddress)
{
BeginPasswordResetAsync(emailAddress);
return View();
}
private Task BeginPasswordResetAsync(string emailAddress)
{
return Task.Run(delegate
{
EmailService.Send(emailAddress);
});
}
}
internal interface IEmailService
{
void Send(string emailAddress);
}
internal class MyControllerTests
{
[TestMethod]
public void BeginPasswordReset_SendsEmail()
{
var mockEmailService = new Mock<IEmailService>();
var controller = new MyController(mockEmailService.Object);
const string emailAddress = "email#domain.com";
controller.BeginPasswordReset(emailAddress);
mockEmailService.Verify(es=>es.Send(emailAddress));
}
}
}
In your task you could set a ManualResetEvent (which our test code blocks on using something like:
Assert.IsTrue(yourEvent.WaitForOne(TimeSpan.FromSecond(<max time you want to wait>), "the event failed to run");
like this:
public void BeginPasswordReset_SendsEmail()
{
const string emailAddress = "email#domain.com";
ManualResetEvent sendCalled= new ManualResetEvent(false);
var mockEmailService = new Mock<IEmailService>();
mockEmailService.Setup(m => m.Send(emailAddress)).Callback(() =>
{
sendCalled.Set();
});
var controller = new MyController(mockEmailService.Object);
controller.BeginPasswordReset(emailAddress);
Assert.IsTrue(sendCalled.WaitOne(TimeSpan.FromSeconds(3)), "Send was never called");
mockEmailService.Verify(es => es.Send(emailAddress));
}
Some quick research it looks like it is possible with MSTest. e.g.
[TestMethod]
public async Task BeginPasswordResetAsync();
{
await BeginPasswordResetAsync("emailAddress");
mockEmailService.Verify...
}