MVVM: Unit test for asynchronous event handler - c#

I have a viewModel with async Task. I don't now how to test it.
public class MyViewModel : BindableBase
{
public MyViewModel()
{
this.PropertyChanged += MyViewModel_PropertyChanged;
}
private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action action = async () => await DoSomething();
action();
}
public const string BeforeKey = "before";
public const string AfterKey = "After";
public string Status { get; private set; } = BeforeKey;
public async Task DoSomething()
{
await Task.Delay(3000);
Status = AfterKey;
}
string bindagleProp;
public string BindagleProp
{
get { return bindagleProp; }
set { SetProperty(ref bindagleProp, value); }
}
}
Here is my test:
[TestMethod]
public async Task TestMyViewModel()
{
MyViewModel viewModel = new MyViewModel();
Assert.AreEqual(viewModel.Status, MyViewModel.BeforeKey, "before check");
viewModel.BindagleProp = "abc";
Assert.AreEqual(viewModel.Status, MyViewModel.AfterKey, "after check");
}
The test failed because it's not waiting to completion of the task.
I DON'T want to use Task.Delay in the unit test, because it's not safety. DoSomething method can has unknown duration time.
Thank you for any help.
Edit:
In fact, The issue is not specific for MVVM, but for any async event handler.
For example:
// class with some logic, can be UI or whatever.
public class MyClassA
{
Size size;
public Size Size
{
get { return size; }
set
{
size = value;
SizeChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler SizeChanged;
}
// this class uses the MyClassA class.
public class MyCunsomerClass
{
readonly MyClassA myClassA = new MyClassA();
public MyCunsomerClass()
{
myClassA.SizeChanged += MyClassA_SizeChanged;
}
public string Status { get; private set; } = "BEFORE";
private async void MyClassA_SizeChanged(object sender, EventArgs e)
{
await LongRunningTaskAsync();
Status = "AFTER";
}
public async Task LongRunningTaskAsync()
{
await Task.Delay(3000);
///await XYZ....;
}
public void SetSize()
{
myClassA.Size = new Size(20, 30);
}
}
Now, I want to test it:
[TestMethod]
public void TestMyClass()
{
var cunsomerClass = new MyCunsomerClass();
cunsomerClass.SetSize();
Assert.AreEqual(cunsomerClass.Status, "AFTER");
}
The test failed.

I asked Stehphen Cleary [The famous professor of asynchronous], and he answered me:
If by "async event handler" you mean an async void event handler,
then no, those aren't testable. However, they are often useful in a UI
application. So what I usually end up doing is having all my async
void methods be exactly one line long. They all look like this:
async void SomeEventHandler(object sender, EventArgsOrWhatever args)
{
await SomeEventHandlerAsync(sender, args);
}
async Task SomeEventHandlerAsync(object sender, EventArgsOrWhatever args)
{
... // Actual handling logic
}
Then the async Task version is unit testable, composable, etc. The
async void handler isn't, but that's acceptable since it no longer
has any real logic at all.
Thanks Stephen! Your idea is excellent!

Ok So first of all, I would move the worker out to an other class and make an interface to it. So that when I run the test I can inject another worker!
public class MyViewModel : BindableBase
{
private IWorker _worker;
private readonly DataHolder _data = new DataHolder(){Test = DataHolder.BeforeKey};
public string Status { get { return _data.Status; } }
public MyViewModel(IWorker worker = null)
{
_worker = worker;
if (_worker == null)
{
_worker = new Worker();
}
this.PropertyChanged += MyViewModel_PropertyChanged;
}
private void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Action action = async () => await _worker.DoSomething(_data);
action();
}
string bindagleProp;
public string BindagleProp
{
get { return bindagleProp; }
set { SetProperty(ref bindagleProp, value); }
}
}
public class DataHolder
{
public const string BeforeKey = "before";
public const string AfterKey = "After";
public string Status;
}
public interface IWorker
{
Task DoSomething(DataHolder data);
}
public class Worker : IWorker
{
public async Task DoSomething(DataHolder data)
{
await Task.Delay(3000);
data.Status = DataHolder.AfterKey;
}
}
Now the inject code would look something like:
[TestMethod]
public async Task TestMyViewModel()
{
TestWorker w = new TestWorker();
MyViewModel viewModel = new MyViewModel(w);
Assert.AreEqual(viewModel.Status, DataHolder.BeforeKey, "before check");
viewModel.BindagleProp = "abc";
Assert.AreEqual(viewModel.Status, DataHolder.AfterKey, "after check");
}
public class TestWorker : IWorker
{
public Task DoSomething(DataHolder data)
{
data.Status = DataHolder.BeforeKey;
return null; //you maybe should return something else here...
}
}

Related

how can I combine await.WhenAny() with GetAwaiter extension method

I want to await a button.click() event. For that created an extension GetAwaiter() method:
public static class ButtonAwaiterExtensions
{
public static ButtonAwaiter GetAwaiter(this Button button)
{
return new ButtonAwaiter()
{
Button = button
};
}
public class ButtonAwaiter : INotifyCompletion
{
public bool IsCompleted
{
get { return false; }
}
public void GetResult()
{
}
public Button? Button { get; set; }
public void OnCompleted(Action continuation)
{
RoutedEventHandler? h = null;
h = (o, e) =>
{
Button!.Click -= h;
continuation();
};
Button!.Click += h;
}
}
}
(I found it here: http://blog.roboblob.com/2014/10/23/awaiting-for-that-button-click/)
With that I can await the Button1.Click() event directly with await Button1; which is great.
BUT I couldn't figure out how to combine this awaitable with someting like await Task.WhenAny()
I tried
await Task.WhenAny(Button1, Button2);
It will tell me that it "cannot convert from Button to Task".
I thougt I found the solution here:
Using `Task.WhenAll` with `INotifyCompletion` by just adding a method
public static async Task GetTask()
{
await this;
}
to my ButtonAwaiterExtensions class, but the keyword this cannot be used in my static class.
I cannot figure out what to return in the method or generally how to await any Button.Click(). Any ideas?
Thanks to Sebastian Schumann comment (how can I combine await.WhenAny() with GetAwaiter extension method) I solved my problem by adding another extension method directly to my ButtonAwaiterExtensions class:
public async static Task AsTask(this Button self) => await self;
Complete solution:
public static class ButtonAwaiterExtensions
{
public static ButtonAwaiter GetAwaiter(this Button button)
{
return new ButtonAwaiter()
{
Button = button
};
}
public class ButtonAwaiter : INotifyCompletion
{
public bool IsCompleted
{
get { return false; }
}
public void GetResult()
{
}
public Button? Button { get; set; }
public void OnCompleted(Action continuation)
{
RoutedEventHandler? h = null;
h = (o, e) =>
{
Button!.Click -= h;
continuation();
};
Button!.Click += h;
}
}
public async static Task AsTask(this Button self) => await self;
}
An alternative (little shorter) Implementation for GetAwaiter might be:
public static TaskAwaiter GetAwaiter(this Button self)
{
ArgumentNullException.ThrowIfNull(self);
TaskCompletionSource tcs = new();
self.Click += OnClick;
return tcs.Task.GetAwaiter();
void OnClick(object sender, EventArgs args)
{
self.Click -= OnClick;
tcs.SetResult();
}
}

how to send value from android-project to share-project?

how to send verificationId from SendOtpCodeAsync() to SendCode_Button_Clicked()
Share Project code
IAuth auth;
auth = DependencyService.Get<IAuth>();
private async void SendCode_Button_Clicked(object sender, EventArgs e)
{
bool result = await auth.SendOtpCodeAsync(PhonenumberEntry.Text);
}
android project code
[assembly: Dependency(typeof(AuthDriod))]
namespace TestApp_MiniApps.Droid
{
public class AuthDriod : PhoneAuthProvider.OnVerificationStateChangedCallbacks, IAuth
{
private TaskCompletionSource<bool> _phoneAuthTcs;
public Task<bool> SendOtpCodeAsync(string phonenumber)
{
_phoneAuthTcs = new TaskCompletionSource<bool>();
Java.Lang.Long num = (Java.Lang.Long)60;
PhoneAuthOptions options =
PhoneAuthOptions.NewBuilder(FirebaseAuth.Instance)
.SetPhoneNumber(phonenumber) // Phone number to verify
.SetTimeout(num, TimeUnit.Seconds) // Timeout and unit
.SetActivity(Platform.CurrentActivity) // Activity (for callback binding)
.SetCallbacks(this) // OnVerificationStateChangedCallbacks
.Build();
PhoneAuthProvider.VerifyPhoneNumber(options);
return _phoneAuthTcs.Task;
}
public override void OnVerificationCompleted(PhoneAuthCredential credential)
{
}
public override void OnVerificationFailed(FirebaseException exception)
{
_phoneAuthTcs?.TrySetResult(false);
}
public override void OnCodeSent(string verificationId, PhoneAuthProvider.ForceResendingToken forceResendingToken)
{
base.OnCodeSent(verificationId, forceResendingToken);
_phoneAuthTcs?.TrySetResult(true);
}
}//end of class
}
share project interface
namespace TestApp_MiniApps.Views.Xamarin.FireBase
{
public interface IAuth
{
Task<bool> SendOtpCodeAsync(string phonenumber);
}//end of class
}
To summarise what Leo had put in the comments you can check what is returned from your method call.
Change from bool to your own class. For the purpose of this answer I will call it OtpResult.
// The new class definition:
public class OtpResult
{
public bool Success { get; set; }
// Define whatever you like here
public string StringValue { get; set; }
}
public interface IAuth
{
Task<OtpResult> SendOtpCodeAsync(string phonenumber);
}

How to catch event while unit testing?

Hi I have a problem testing an event using NUnit. I'm not even sure if this should be unit or functional test. Let me show you the sample class first (I'm trying to test OnValueInjected event):
public class Foo
{
private IBar CurrentBar { get; set; }
public event EventHandler<MoveEventArgs> OnValueInjected;
public Foo()
{
StartFoo();
}
private async void StartFoo()
{
await Task.Factory.StartNew(() =>
{
while (State != FooState.Finished)
{
IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value
OnValueInjected?.Invoke(this, new ResultEventArgs(result));
// .. rest of the loop
}
});
}
public void InjectValue(int a, int b)
{
CurrentBar.Inject(a,b);
}
}
So, basically what I'm trying to do is to subscribe to the event, call InjectValue and check if the event was called. Like this:
[Test]
public void FooOnValueInjectedTest()
{
bool OnValueInjectedWasRasied = false;
IFoo foo = new Foo();
foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true;
foo.InjectValue(0,0);
Assert.AreEqual(true, OnValueInjectedWasRasied);
}
Pretty straightforward, BUT it looks like InjectValue is too slow. The test is failing..I think it's too slow, because when I add Thread.Sleep between InjectValue and Assert to works.
foo.InjectValue(0,0);
Thread.Sleep(1000);
Assert.AreEqual(true, OnValueInjectedWasRasied);
Is there a better way to test such an event? Thanks
I fixed my class, so it's like that now:
public class Foo
{
private AutoResetEvent AutoReset { get; }
private IBar CurrentBar { get; set; }
public event EventHandler<MoveEventArgs> OnValueInjected;
public Foo()
{
AutoReset = new AutoResetEvent(false);
StartFoo();
}
private async void StartFoo()
{
await Task.Factory.StartNew(() =>
{
while (State != FooState.Finished)
{
IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value
OnValueInjected?.Invoke(this, new ResultEventArgs(result));
AutoReset.Set();
// .. rest of the loop
}
});
}
public void InjectValue(int a, int b)
{
if (CurrentBar.Inject(a,b))
{
AutoReset.WaitOne();
}
}
}
I believe this is a problem of asynchronous calling. Whenever you have an async method in an NUnit test, it doesn't wait for it to be done as no one is actually waiting for it to be done and return the result. Instead, you have to do a .Wait on the async method to force the test to wait for it to be done.
I did not write this code in a code editor so it may not be perfect but that's the basic idea.
public class Foo
{
private AutoResetEvent AutoReset { get; }
private IBar CurrentBar { get; set; }
public event EventHandler<MoveEventArgs> OnValueInjected;
public Foo()
{
AutoReset = new AutoResetEvent(false);
StartFoo();
}
private async void StartFoo()
{
await Task.Factory.StartNew(() =>
{
while (State != FooState.Finished)
{
IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value
OnValueInjected?.Invoke(this, new ResultEventArgs(result));
AutoReset.Set();
// .. rest of the loop
}
});
}
public async void InjectValue(int a, int b)
{
if (CurrentBar.Inject(a,b))
{
AutoReset.WaitOne();
}
}
}
Then in your test method in the ACT you do a .Wait
[Test]
public void FooOnValueInjectedTest()
{
// Arrange
bool OnValueInjectedWasRasied = false;
IFoo foo = new Foo();
foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true;
// Act
foo.InjectValue(0,0).Wait();
// Assert
Assert.AreEqual(true, OnValueInjectedWasRasied);
}

How to create a thread in c#

Can I create a class that inherited from thread class in c#, for my Windows Phone application.
For example :
if my class name is 'MyClass' I want to start the thread as new MyClass().Start();
Like in following Java example
public class TagIndexer
{
private static class Task
{
private String docId;
private String tags;
private String extension;
public Task(String docId, String tags, String extension)
{
this.docId = docId;
this.tags = tags;
this.extension = extension;
}
}
private static final LinkedList<Task> queue = new LinkedList<Task>();
private static boolean isWorking = false;
private static class TaskRunner extends Thread
{
#Override
public void run()
{
while (true)
{
Task task;
synchronized (queue)
{
task = queue.poll();
if (null == task)
{
isWorking = false;
break;
}
isWorking = true;
}
/*
* PROCESSING CODE
*/
}
}
}
public static void addDocument(int docId, String tags, String extension)
{
Task task = new Task(Integer.toString(docId), tags, extension);
synchronized (queue)
{
queue.add(task);
if (!isWorking)
{
new TaskRunner().start();
}
}
}
}
new MyClazz().Start();
-
public abstract class MyThread
{
public abstract void Run();
public void Start()
{
new Thread(Run).Start();
}
}
public class MyClazz : MyThread
{
public override void Run()
{
Console.WriteLine("Hello World");
}
}
On Windows Phone, Thread is a sealed class, therefore you cannot inherit from it. If you want to keep the task-based approach, you can just create a class that will wrap a thread instance. Something like:
public abstract class Task
{
protected Thread InternalThread { get; set; }
protected abstract void Run();
public void Start()
{
this.InternalThread = new Thread(this.Run);
this.InternalThread.Start();
}
}
Of course, it's just an example. You would have to add some synchronization mechanism to prevent the Start method from creating multiple threads if called more than once.
Then you can inherit it to create custom tasks:
public class MyTask : Task
{
protected override void Run()
{
// Do something
}
}
See this article about BackgroundAgent from MSDN:
Background Agents Overview for Windows Phone

Adding cancel ability and exception handling to async code

I have this sample code for async operations (copied from the interwebs)
public class LongRunningTask
{
public LongRunningTask()
{
//do nowt
}
public int FetchInt()
{
Thread.Sleep(2000);
return 5;
}
}
public delegate TOutput SomeMethod<TOutput>();
public class GoodPerformance
{
public void BeginFetchInt()
{
LongRunningTask lr = new LongRunningTask();
SomeMethod<int> method = new SomeMethod<int>(lr.FetchInt);
// method is state object used to transfer result
//of long running operation
method.BeginInvoke(EndFetchInt, method);
}
public void EndFetchInt(IAsyncResult result)
{
SomeMethod<int> method = result.AsyncState as SomeMethod<int>;
Value = method.EndInvoke(result);
}
public int Value { get; set; }
}
Other async approaches I tried required the aysnc page attribute, they also seemed to cancel if other page elements where actioned on (a button clicked), this approach just seemed to work.
I’d like to add a cancel ability and exception handling for the longRunningTask class, but don’t erm, really know how.
In example:
public class ValueEventArgs : EventArgs
{
public int Value { get;set;}
}
public class ExceptionEventArgs : EventArgs
{
public Exception Exception { get;set;}
}
public class LongRunningTask
{
private bool canceled = false;
public event EventHandler<ValueEventArgs> Completed = delegate {}
public event EventHandler<ExceptionEventArgs> GotError = delegate {}
public void Cancel()
{
canceled = true;
}
public void FetchInt()
{
try
{
int result = 0;
for (int i = 0; i < 1000; i++)
{
if (canceled)
return;
result++;
}
Completed(this, new ValueEventArgs {Value = result});
}
catch(Exception exc)
{
GotError(this, new ExceptionEventArgs { Exception = exc });
}
}
public void BeginFetchInt()
{
ThreadPool.QueueUserWorkItem(i => FetchInt());
}
}
And somewhere:
LongRunningTask task = new LongRunningTask();
task.Completed +=new EventHandler<ValueEventArgs>(task_Completed);
task.GotError +=new EventHandler<ExceptionEventArgs>(task_GorError);
task.BeginFetchInt();
//in any moment until it calculates you may call:
task.Cancel();

Categories

Resources