Skip to content

Commit 799558c

Browse files
committed
Add unit tests for throwing async commands
1 parent dba9c41 commit 799558c

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

tests/CommunityToolkit.Mvvm.UnitTests/CommunityToolkit.Mvvm.UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8+
<PackageReference Include="Dbs.Signed3.Nito.AsyncEx.Context" Version="5.0.0" />
89
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
910
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
1011
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CommunityToolkit.Mvvm.Input;
1111
using CommunityToolkit.Mvvm.UnitTests.Helpers;
1212
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
using Nito.AsyncEx;
1314

1415
namespace CommunityToolkit.Mvvm.UnitTests;
1516

@@ -320,6 +321,43 @@ private static async Task Test_AsyncRelayCommand_AllowConcurrentExecutions_TestL
320321
Assert.AreSame(args.Item2, EventArgs.Empty);
321322
}
322323

324+
// See https://github.com/CommunityToolkit/dotnet/pull/251
325+
[TestMethod]
326+
public async Task Test_AsyncRelayCommand_EnsureExceptionThrown()
327+
{
328+
const int delay = 500;
329+
330+
Exception? executeException = null;
331+
Exception? executeAsyncException = null;
332+
333+
AsyncRelayCommand command = new(async () =>
334+
{
335+
await Task.Delay(delay);
336+
337+
throw new Exception(nameof(Test_AsyncRelayCommand_EnsureExceptionThrown));
338+
});
339+
340+
try
341+
{
342+
// Use AsyncContext to test async void methods https://stackoverflow.com/a/14207615/5953643
343+
AsyncContext.Run(async () =>
344+
{
345+
command.Execute(null);
346+
347+
await Task.Delay(delay * 2); // Ensure we don't escape `AsyncContext` before command throws Exception
348+
});
349+
}
350+
catch (Exception e)
351+
{
352+
executeException = e;
353+
}
354+
355+
executeAsyncException = await Assert.ThrowsExceptionAsync<Exception>(() => command.ExecuteAsync(null));
356+
357+
Assert.AreEqual(nameof(Test_AsyncRelayCommand_EnsureExceptionThrown), executeException?.Message);
358+
Assert.AreEqual(nameof(Test_AsyncRelayCommand_EnsureExceptionThrown), executeAsyncException?.Message);
359+
}
360+
323361
[TestMethod]
324362
public async Task Test_AsyncRelayCommand_ThrowingTaskBubblesToUnobservedTaskException()
325363
{
@@ -332,7 +370,7 @@ static async Task TestMethodAsync(Action action)
332370

333371
async void TestCallback(Action throwAction, Action completeAction)
334372
{
335-
AsyncRelayCommand command = new(() => TestMethodAsync(throwAction));
373+
AsyncRelayCommand command = new(() => TestMethodAsync(throwAction), AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
336374

337375
command.Execute(null);
338376

tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand{T}.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CommunityToolkit.Mvvm.Input;
1111
using CommunityToolkit.Mvvm.UnitTests.Helpers;
1212
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
using Nito.AsyncEx;
1314

1415
namespace CommunityToolkit.Mvvm.UnitTests;
1516

@@ -241,6 +242,58 @@ private static async Task Test_AsyncRelayCommandOfT_AllowConcurrentExecutions_Te
241242
_ = Assert.ThrowsException<InvalidCastException>(() => command.Execute(new object()));
242243
}
243244

245+
// See https://github.com/CommunityToolkit/dotnet/pull/251
246+
[TestMethod]
247+
public async Task Test_AsyncRelayCommandOfT_EnsureExceptionThrown()
248+
{
249+
const int delay = 500;
250+
251+
Exception? executeException = null;
252+
Exception? executeTException = null;
253+
Exception? executeAsyncException = null;
254+
255+
AsyncRelayCommand<int> command = new(async delay =>
256+
{
257+
await Task.Delay(delay);
258+
259+
throw new Exception(nameof(Test_AsyncRelayCommandOfT_EnsureExceptionThrown));
260+
});
261+
262+
try
263+
{
264+
AsyncContext.Run(async () =>
265+
{
266+
command.Execute((object)delay);
267+
268+
await Task.Delay(delay * 2);
269+
});
270+
}
271+
catch (Exception e)
272+
{
273+
executeException = e;
274+
}
275+
276+
try
277+
{
278+
AsyncContext.Run(async () =>
279+
{
280+
command.Execute(delay);
281+
282+
await Task.Delay(delay * 2);
283+
});
284+
}
285+
catch (Exception e)
286+
{
287+
executeTException = e;
288+
}
289+
290+
executeAsyncException = await Assert.ThrowsExceptionAsync<Exception>(() => command.ExecuteAsync(delay));
291+
292+
Assert.AreEqual(nameof(Test_AsyncRelayCommandOfT_EnsureExceptionThrown), executeException?.Message);
293+
Assert.AreEqual(nameof(Test_AsyncRelayCommandOfT_EnsureExceptionThrown), executeTException?.Message);
294+
Assert.AreEqual(nameof(Test_AsyncRelayCommandOfT_EnsureExceptionThrown), executeAsyncException?.Message);
295+
}
296+
244297
[TestMethod]
245298
public async Task Test_AsyncRelayCommandOfT_ThrowingTaskBubblesToUnobservedTaskException()
246299
{
@@ -253,7 +306,7 @@ static async Task TestMethodAsync(Action action)
253306

254307
async void TestCallback(Action throwAction, Action completeAction)
255308
{
256-
AsyncRelayCommand<string> command = new(s => TestMethodAsync(throwAction));
309+
AsyncRelayCommand<string> command = new(s => TestMethodAsync(throwAction), AsyncRelayCommandOptions.FlowExceptionsToTaskScheduler);
257310

258311
command.Execute(null);
259312

0 commit comments

Comments
 (0)