Comments (7)
It appears the NUnit team agrees with you. :-) They've fixed it in 3.11.
from mvvm.
Nito.Mvvm
assumes it is running on a single-threaded context, like a UI context. To put it another way, all of the types have a "thread affinity" - they can only be accessed from the thread they were created on. The exception you're seeing is a cross-thread exception.
Using async
/await
in both NUnit and Console apps can switch threads. In order to prevent this, you'll need to use an async
-compatible single-threaded context like AsyncContext
. Something like this:
[Test]
public void Fixed()
{
AsyncContext.Run(async () =>
{
AsyncCommand command = new AsyncCommand(
async () => { await Task.Delay(1000); });
await command.ExecuteAsync(null);
});
}
from mvvm.
Hm, I thought not using ConfigureAwait(false)
would prevent switching threads. I've already been playing around with [SingleThreaded]
and [Apartment(ApartmentState.STA)]
to no avail, but your solution seems to do the trick.
Thanks a lot!
from mvvm.
🧟♀️ Zombie thread resurrected for posterity. 🧟
Another option is to remove thread affinity requirement by duplicating Nito.Mvvm.Core's StringCanExecuteChanged
(and/or WeakCanExecuteChanged
) to remove thread affinity:
public sealed class AsyncCommandCanExecuteChanged : ICanExecuteChanged
{
/// <summary>
/// A factory function for creating CanExecuteChanged objects.
/// </summary>
/// <param name="sender">The object to emit the events.</param>
/// <returns>The newly created <see cref="AsyncCommandCanExecuteChanged"/> object.</returns>
public static ICanExecuteChanged Create( object sender )
{
return new AsyncCommandCanExecuteChanged( sender );
}
/// <summary>
/// The sender of the <c>ICommand.CanExecuteChanged</c> event.
/// </summary>
private readonly object _sender;
/// <summary>
/// The collection of delegates for <see cref="CanExecuteChanged"/>.
/// </summary>
private event EventHandler CanExecuteChangedEvent;
/// <summary>
/// Creates a new strong-event implementation of <c>ICommand.CanExecuteChanged</c>.
/// </summary>
/// <param name="sender">The sender of the <c>ICommand.CanExecuteChanged</c> event.</param>
public AsyncCommandCanExecuteChanged( object sender )
{
_sender = sender;
}
/// <inheritdoc />
/// <summary>
/// Provides notification that the result of <c>ICommand.CanExecute</c> may be different.
/// </summary>
public event EventHandler CanExecuteChanged
{
add => CanExecuteChangedEvent += value;
remove => CanExecuteChangedEvent -= value;
}
/// <inheritdoc />
/// <summary>
/// Raises the <see cref="CanExecuteChanged" /> event.
/// </summary>
public void OnCanExecuteChanged()
{
CanExecuteChangedEvent?.Invoke( _sender, EventArgs.Empty );
}
}
And then pass it into your commands at construction, e.g.:
DeleteSelectedObjectsCommand = new CustomAsyncCommand(
DeleteSelectedObjectsAsync,
_ => HasSelection,
AsyncCommandCanExecuteChanged.Create );
Then the test can await as usual:
[Test]
public async Task TestDeletion()
{
// ... assembly here ...
await _viewModel.DeleteSelectedObjectsCommand.ExecuteAsync( null );
// ... asserts here ...
}
Of course you may find you get bit by thread affinity being off, but it could also be not required in your context, as (I think) it is in mine. (Perhaps Stephen will reply that this is ill-advised, but I'll wait and see. :-) )
from mvvm.
Well, yeah, the thread affinity was deliberately put in because lots of the code assumes it is running on the same thread. More specifically, events are raised using the simple pattern rather than using an extra variable, and all private state variables are completely unprotected (no lock
, volatile
, or barriers of any kind).
So, disabling the thread affinity guard voids your warranty. :)
I would still recommend using AsyncContext.Run
, or an alternative test framework like xUnit that provides its own context.
from mvvm.
Ah, so you think this is a defect in NUnit?
from mvvm.
Not a defect per se. Just a different way of doing things that makes testing asynchronous UI code harder.
from mvvm.
Related Issues (20)
- AsyncCommand must be cast to IAsyncCommand or ICommand to access CanExecute and CanExecuteChanged HOT 1
- Examine Prism's AsyncCommand discussion
- NotifyTask - delay in loading data HOT 7
- Task exceptions not "handled" HOT 2
- Bind to Execution.Result HOT 1
- NotifyTask concern HOT 5
- AsyncCommand exceptions and Execution HOT 1
- PropertyChangedEventArgsCache does not exist in the current context HOT 1
- AsyncCommand<T> ? HOT 3
- How to use AsyncCommand canExecuteChangedFactory? HOT 3
- Package release/prerelease status HOT 3
- AyncCommand throws on the UI-Thread
- CancelCommand.Wrap does not propagate IsCancelled information
- Put up release package on nuget.org HOT 1
- Determine whether CA1001 is correct or not.
- Provide interface for NotifyTask HOT 2
- Provide stable release of Nito.Mvvm.Async
- PropertyChangedEventArgsCache HOT 2
- Why does Nito.Mvvm.CalculatedProperties need to depend on Nito.Mvvm.Core? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mvvm.