I'm currently attempting to write unit tests for my bot, however, the test always fail when attempting to get a response. I've created a mock test which inherits DialogTestBase.
[TestClass]
public class Tests : DialogTestBase
{
[TestMethod]
public async Task TestDialogTest()
{
await TestDialogFlow(new TestDialog());
}
private async Task TestDialogFlow(IDialog<object> echoDialog)
{
// arrange
var toBot = DialogTestBase.MakeTestMessage();
toBot.From.Id = Guid.NewGuid().ToString();
toBot.Text = "Hi";
Func<IDialog<object>> MakeRoot = () => echoDialog;
using (new FiberTestBase.ResolveMoqAssembly(echoDialog))
using (var container = Build(Options.MockConnectorFactory | Options.ScopedQueue, echoDialog))
{
IMessageActivity toUser = await GetResponse(container, MakeRoot, toBot);
Assert.IsTrue(toUser.Text.StartsWith("Hello"));
}
}
private async Task<IMessageActivity> GetResponse(IContainer container, Func<IDialog<object>> makeRoot, IMessageActivity toBot)
{
using (var scope = DialogModule.BeginLifetimeScope(container, toBot))
{
DialogModule_MakeRoot.Register(scope, makeRoot);
// act: sending the message
await Conversation.SendAsync(toBot, makeRoot);
return scope.Resolve<Queue<IMessageActivity>>().Dequeue();
}
}
}
The dialog i'm testing is:
[Serializable]
public class TestDialog : IDialog
{
public async Task StartAsync(IDialogContext context)
{
context.Wait(ProcessMessage);
}
public async Task ProcessMessage(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
await context.PostAsync("Hello. I'm a bot");
await context.PostAsync("How can I help?");
context.Wait(ProcessRequest);
}
public async Task ProcessRequest(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var request = await argument;
await context.PostAsync($"You asked the following question: {request}");
context.Done(true);
}
}
When I run the test I get the following error:
Autofac.Core.DependencyResolutionException: An exception was thrown while executing a resolve operation. See the InnerException for details. ---> Invalid URI: The format of the URI could not be determined. (See inner exception for details.) --->System.UriFormatException: Invalid URI: The format of the URI could not be determined.
The problem happens in the GetResponse method when we send the request. Any help would be greatly appreciated.
The GetResponse method should actually call SendAsync using scope and toBot as parameters:
await Conversation.SendAsync(toBot, makeRoot);
I tried the code you've shared and after making this change, the test passes.
Related
I have the following SignalR Hub with in-memory connection management.
[Authorize]
public class ChatHub : Hub
{
private static readonly ConnectionMapping<string> _connections =
new ConnectionMapping<string>();
private readonly IMessageRepository _messagesRepository;
public ChatHub(IMessageRepository messageRepository)
{
_messagesRepository = messagesRepository;
}
public override async Task OnConnectedAsync()
{
var name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
//await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var name = Context.User.Identity.Name;
_connections.Remove(name, Context.ConnectionId);
//await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnDisconnectedAsync(exception);
}
public async void SendMessage(ChatMessage chatMessage)
{
var name = Context.User.Identity.Name;
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
var availableConnections = _connections.GetConnections(name);
if (availableConnections.Any())
{
foreach (var connectionId in availableConnections)
{
Clients.Client(connectionId).SendAsync("ReceiveMessage", chatHistoryMessage);
}
}
else
{
}
}
}
However, when executing the code the following line
Clients.Client(connectionId).SendAsync("ReceiveMessage", 1);
raises an object disposed error on Clients.
This issue started happening when I added the repository line:
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
SaveMessageAsync method:
public async Task<ChatHistory> SaveMessageAsync (ChatMessage chatMessage)
{
using (var conn = new SqlConnection(ConnProvider.ConnectionString))
{
await conn.OpenAsync();
return (await conn.QueryAsync<ChatHistory>("[mob].[spSaveChatMessage]",
new
{
..
},
commandType: CommandType.StoredProcedure)).FirstOrDefault();
}
}
Why would my Clients object be disposed? If I wait with the debugger that issue never happens.
It looks like the SendMessage method should be an async Task rather than an async void.
The issue could be caused by the way the SignalR framework runs async voids.
See this article for a good overview.
Async methods returning void don’t provide an easy way to notify the
calling code that they’ve completed.
Environment
Windows 10 Professional
.NET Core Console Application
Code
I have an abstracted message receiver that looks like this. In this code the entity is the name of the Subscription (e.g. user).
public class AzureMessageReceiver : ITdlMessageReceiver
{
private readonly ServiceBusConnection serviceBusConnection;
private readonly ILogger<AzureMessageReceiver> logger;
public AzureMessageReceiver(ServiceBusConnection serviceBusConnection, ILogger<AzureMessageReceiver> logger)
{
this.serviceBusConnection = serviceBusConnection;
this.logger = logger;
}
public async Task<TdlMessage<T>> ReceiveAsync<T>(string topic, string entity) where T : class
{
try
{
var subscriptionPath = EntityNameHelper.FormatSubscriptionPath(topic, entity);
var messageReceiver = new MessageReceiver(serviceBusConnection, subscriptionPath, ReceiveMode.ReceiveAndDelete);
var message = await messageReceiver.ReceiveAsync();
if (message == null)
{
return null;
}
var messageString = Encoding.UTF8.GetString(message.Body);
return JsonConvert.DeserializeObject<TdlMessage<T>>(messageString);
}
catch (Exception ex)
{
logger.LogError(ex, "Error receiving Azure message.");
return null;
}
}
}
The injected ServiceBusConnection is constructed like this. NOTE: this same connection initialization works to write messages to the same Topic and Subscription.
services.AddSingleton(serviceProvider =>
new ServiceBusConnection(configuration[$"{DurableCommunicationKey}:AzureConnectionString"]));
UPDATE: here is the code that wraps the call to the receiver class and is the controller for receiving messages:
static async void Receive(ITdlMessageReceiver receiver, ILogger logger)
{
while (true)
{
var message = await receiver.ReceiveAsync<TdlMessage<object>>(topic, entity);
if (message != null)
{
logger.LogDebug($"Message received. Topic: {topic}. Action: {Enum.GetName(typeof(TopicActions), message.Action)}. Message: {JsonConvert.SerializeObject(message)}.");
}
Thread.Sleep(sleepTime);
}
}
Problem
Every time I execute this line var message = await messageReceiver.ReceiveAsync(); it just crashes the Console app. No Exception and nothing in Event Viewer.
What I've Tried
Using the Secondary Connection String from the ASB
Providing a timeout like messageReceiver.ReceiveAsync(TimeSpan.FromMinutes(1));
Changing the injected topic from just the name of the topic to the entire URL of the topic (e.g. https://{...}.servicebus.windows.net/{topicName})
Changing the ReceiveMode to PeekLock
Tacking on ConfigureAwait(false) to the ReceiveAsync call.
Changing the timeout to TimeSpan.Zero. NOTE: this does not crash the app but actually throws an Exception that gets logged.
async void should be converted to an async Task as well as you should be awaiting Task.Delay instead of invoking Thread.Sleep. If going async you need to go async all the way
static async Task Receive(ITdlMessageReceiver receiver, ILogger logger) {
while (true) {
var message = await receiver.ReceiveAsync<TdlMessage<object>>(topic, entity);
if (message != null) {
logger.LogDebug($"Message received. Topic: {topic}. Action: {Enum.GetName(typeof(TopicActions), message.Action)}. Message: {JsonConvert.SerializeObject(message)}.");
}
await Task.Delay(sleepTime);
}
}
Try making the code async all the way through, yes, but as a console application (single thread) you will be allowed to call Wait() on the Receive method in Main as it is not mixing calls that would cause problem with the async flow.
public static void Main(string[] args) {
//...
//...
//...
Receive(receiver, logger).Wait();
}
Reference Async/Await - Best Practices in Asynchronous Programming
I am using Visual Studio 2013 and MSTest for unit testing my async method. I have async method as below:
public async Task<Client> AddClientAsync(Client newClient)
{
newClient.ObjectState = ObjectState.Added;
base.Insert(newClient);
await unitOfWorkAsync.SaveChangesAsync();
return newClient;
}
UnitOfWorkAsync SaveChangesAsync() method:
public Task<int> SaveChangesAsync()
{
return _dataContext.SaveChangesAsync();
}
Data Context SaveChangesAsync() method:
public override async Task<int> SaveChangesAsync()
{
return await this.SaveChangesAsync(CancellationToken.None);
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
SyncObjectsStatePreCommit();
var changesAsync = await base.SaveChangesAsync(cancellationToken);
SyncObjectsStatePostCommit();
return changesAsync;
}
I have written following unit test method to test it.
[TestMethod]
public async Task AddClientAsync_Success()
{
Client client = new Client()
{
Id=2,
FirstName="John",
LastName="Tylor",
MaritalStatusId=1,
RecordStatus=1
};
var result = await clientAppService.AddClientAsync(client);
Assert.IsNotNull(result);
}
When I run/debug test case control reaches till below line and never returns. Its kind of getting stuck and keeps waiting endlessly at this line and test never completes.
await unitOfWorkAsync.SaveChangesAsync();
Any idea what am I missing here?
Update: It executes SaveChangesAsync() method which also returns result but post that test never completes.
Today I read a lot about async/await and it completely blew my mind.
I can't understand why the following test passed.
[Test]
public void Test()
{
var listener = new AsyncHttpListener();
listener.ListeningAsync();
try
{
new WebClient().DownloadString("http://localhost:8080/");
}
catch (Exception)
{
}
listener.Close();
}
public class AsyncHttpListener
{
private readonly HttpListener listener;
public AsyncHttpListener()
{
listener = new HttpListener();
listener.Prefixes.Add("http://localhost:8080/");
listener.Start();
}
public void Close()
{
listener.Close();
}
public async void ListeningAsync()
{
var context = await listener.GetContextAsync();
HandleContext(context);
}
private void HandleContext(HttpListenerContext context)
{
throw new Exception("test excpetion");
}
}
Test passed, but output contains:
System.Exception
test excpetion
at AsyncHttpListenerTest.AsyncHttpListener.HandleContext(HttpListenerContext context) in AsyncHttpListener.cs: line 30
at AsyncHttpListenerTest.AsyncHttpListener.d__0.MoveNext() in AsyncHttpListener.cs: line 25
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.b__1(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
I expect that exception will be transmitted from task thread (HandleContext() method) to caller context and test fail. How can i get this behavior?
Make your method async Task instead of async void, and make your test method async Task instead of void:
public async Task ListeningAsync()
{
var context = await listener.GetContextAsync();
HandleContext(context);
}
[Test]
public async Task Test()
{
var listener = new AsyncHttpListener();
await listener.ListeningAsync();
try
{
new WebClient().DownloadString("http://localhost:8080/");
}
catch (Exception)
{
}
listener.Close();
}
There are several good reasons to avoid async void. Error handling is one of them. Errors raised from async void methods go straight to the SynchronizationContext that was current when the method started.
The reason your test passed is because async methods may return to their caller before they complete. The test runner sees the test method return (without throwing an exception yet), and marks it as "passed". If you return Task from your test method, then the test runner knows to wait for the Task to complete before considering the test complete.
public class FooHandler : HttpTaskAsyncHandler
{
public override async Task ProcessRequestAsync(HttpContext context)
{
return await new AdRequest().ProcessRequest();
// getting error here. "Return type of async type is void"
}
}
public class FooRequest
{
public async Task<String> ProcessRequest()
{
//return await "foo"; obviously nothing to wait here
}
}
I want to make a async handler and just want to return a string. How can i get this working? and is there a concise reference to work with Async methods and Tasks?
A few points:
You can await any Task, not just ones returned from async methods.
async methods wrap their returned value into a Task<TResult>; if there is no return value, they wrap the return itself into a Task.
There are several convenience methods available, e.g., Task.FromResult, if you don't need the overhead of an async method.
Only make a method async if you have to use await in it. If you don't need to make the method async, don't.
You may find my async/await intro helpful.
public class FooHandler : HttpTaskAsyncHandler
{
public override Task ProcessRequestAsync(HttpContext context)
{
return new AdRequest().ProcessRequest();
}
}
public class AdRequest
{
public Task<String> ProcessRequest()
{
return Task.FromResult("foo");
}
}
You shouldn't "return" the Task, the compiler will do it implicitly as it is an async function:
public override async Task ProcessRequestAsync(HttpContext context)
{
await new AdRequest().ProcessRequest();
}
public async Task<String> ProcessRequest()
{
return "foo";
}
This is another way, closer to what you were trying to do: (without async/await)
public override Task ProcessRequestAsync(HttpContext context)
{
return new AdRequest().ProcessRequest();
}
public Task<String> ProcessRequest()
{
return Task.Return("foo");
}
A general reference to async is here
Essentially adding the async modifier to a method, makes it return a Task implicitly. If you return an int, it will turn it into a Task<int>. await does the opposite, turning a Task<int> into an int.
This is a truly asynchronous method:
public Task<string> ProcessRequest()
{
var textFile = File.OpenText("file.txt");
var readTask = textFile.ReadToEndAsync();
readTask.ContinueWith(previousTask => textFile.Dispose());
return readTask;
}
If you run this method with a large file or a file on a slow drive the execution will return to caller long before file reading ends. In Stephen Cleary's example the caller will get back control only when the result ("foo") is finished calculating.
Dispose must be in ContinueWith because the method execution will return to caller before file reading is complete so file can't be closed in ProcessRequest method.
One can of course start their own task.
public Task<string> ProcessRequest(CancellationToken cancellationToken)
{
var readTask = Task.Run(() =>
{
using (var textFile = File.OpenText("file.txt"))
{
var text = textFile.ReadToEnd();
cancellationToken.ThrowIfCancellationRequested();
var processedText = text.Replace("foo", "bar");
return processedText;
}
});
return readTask;
}
It is a good practice to have a CancellationToken and periodically check if cancellation was requested to allow long running operarions to be cancelled.
Edit 1
As #Stephen Cleary highlighted the first sample and this result in approximately or maybe exactly the same CIL:
public async Task<string> ProcessRequest()
{
using (var textFile = File.OpenText("file.txt"))
{
var s = await textFile.ReadToEndAsync();
return s;
}
}
Basically the compiler will transform the code following await textFile.ReadToEndAsync() into ContinueWith.
Each syntax has its benefits, my preference is that 1-2 lines (i.e. dispose and log) go into ContinueWith, more complex continuation uses await.