How to Unit Test a void method with a Task inside - c#

I have a graphic method CancelChanges() used and called by a ViewModel.
I want to test this method but we have a Task inside.
We use a Task to not freeze the UI.
My test method needs to wait the result of this Task to check the result.
The code is:
public override void CancelChanges()
{
Task.Run(
async () =>
{
this.SelectedWorkflow = null;
AsyncResult<IncidentTypeModel> asyncResult = await this.Dataprovider.GetIncidentTypeByIdAsync(this.Incident.Id);
Utils.GetDispatcher().Invoke(
() =>
{
if (asyncResult.IsError)
{
WorkflowMessageBox.ShowException(
MessageHelper.ManageException(asyncResult.Exception));
}
else
{
this.Incident = asyncResult.Result;
this.Refreshdesigner();
this.HaveChanges = false;
}
});
});
}
And my test method:
/// <summary>
/// A test for CancelChanges
/// </summary>
[TestMethod]
[TestCategory("ConfigTool")]
public void CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
this._target.CancelChanges();
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
}
How can we do to have my test that is waiting the result of the Task?
Thanks.

Make the CancelChanges method return a Task, and then await this or set up a continuation in the test method. Some this like
public override Task CancelChanges()
{
return Task.Factory.StartNew(() =>
{
// Do stuff...
});
}
notice the change from Task.Run to Task.Factory.StartNew. This is a better way of starting tasks in such cases. Then in the test method
[TestMethod]
[TestCategory("ConfigTool")]
public void CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
this._target.CancelChanges().ContinueWith(ant =>
{
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
});
}
You could also mark the test method as async and use await in the test method to do the same thing.
I hope this helps.

I would take the refactoring one step further, and if possible avoid using Task.Run. Since all you do is await and then invoke work on the UI Thread, I would do the following:
public override Task CancelChanges()
{
this.SelectedWorkflow = null;
AsyncResult<IncidentTypeModel> asyncResult = await this.Dataprovider.GetIncidentTypeByIdAsync(this.Incident.Id);
if (asyncResult.IsError)
{ WorkflowMessageBox.ShowException(MessageHelper.ManageException(asyncResult.Exception));
}
else
{
this.Incident = asyncResult.Result;
this.Refreshdesigner();
this.HaveChanges = false;
}
});
});
}
And the test method:
[TestMethod]
[TestCategory("ConfigTool")]
public async Task CancelChangesTest()
{
string storexaml = this._target.Incident.WorkflowXamlString;
this._target.Incident.WorkflowXamlString = "dsdfsdgfdsgdfgfd";
var cancelChanges = await this._target.CancelChanges();
Assert.IsTrue(storexaml == this._target.Incident.WorkflowXamlString);
Assert.IsFalse(this._target.HaveChanges);
}

Related

(A)wait for class to finish instantiate

I try to wait for the class to be finished with instantiate.
My architecture is the following. Cook is inheriade from CookChief.
And if I instantiate cook, CookChief is creating himself, but CookChief is calling 1 other class named Cookhelper the cookhelper is waiting for a input and for this input method i want to wait in Cook.
The thing is iam creating this in MVVM Galasoft and my entry point is the CookViewmodel, with a relaycommand.
In the code below you can see my architecture. To say it short I want to wait until this bool processed = await Task.Run(() => ValidateForDeviceId()); is finished.
My first step was to outsource the constructer of each class. And create a init method.
This is my code:
public CookViewModel()
{
startCookButtonCommand = new RelayCommand(Cook);
}
private async Task Cook()
{
cook.Init();
}
public class Cook : CookChief
{
public Cook()
{
}
public async Task Init()
{
await this.CookChiefInit();
//here I want to wait until CookChiefInit is finished
Cooking();
}
public void Cooking()
{
MessageBox.Show("Input received");
}
}
Now the Cookchief:
public Cookchief()
{
}
protected async Task CookchiefInit()
{
this.Cookhelper = new Cookhelper();
Cookhelper.CookHelperInit();
}
And in the CookHelper we do this:
public CookHelper()
{
}
public void CookHelperInit()
{
this.driverWindow = new DriverWindow();
startProc();
}
private async void startProc()
{
ShowOrCloseDriverWindow(true);
//this is the task what we wait for before we can repeat
bool processed = await Task.Run(() => ValidateForDeviceId());
if(processed)
{
ShowOrCloseDriverWindow(false);
}
else
{
MessageBox.Show("DriverError");
}
}
private bool ValidateForDeviceId()
{
for (; ; )
{
this.deviceId = Input.deviceId;
if (deviceId > 0)
{
break;
}
}
return true;
}
Per the discussion in the comments, the problem here was that the initialization routine mixed synchronous and asynchronous methods and calls. Additionally, some async methods were called without the await keyword. The solution was to make all calls asynchronous and await them.
cook.Init() needs an await:
private async Task Cook()
{
await cook.Init();
}
In CookchiefInit(), the CookHelperInit() call needs to be awaited:
protected async Task CookchiefInit()
{
this.Cookhelper = new Cookhelper();
Cookhelper.CookHelperInit();
}
In order to await CookHelperInit(), it needs to be made asynchronous. The startProc() call is to an async method, so it must also be awaited:
public async Task CookHelperInit()
{
this.driverWindow = new DriverWindow();
await startProc();
}

Testing property set by async method

I try to test a class with NUnit that contains async methods. I don't know how to do it in a correct way.
I have a class with that looks like this:
public class EditorViewModel:INotifyPropertyChanged
{
public void SetIdentifier(string identifier)
{
CalcContentAsync();
}
private async void CalcContentAsync()
{
await SetContentAsync();
DoSomething();
}
private async Task SetContentAsync()
{
Content = await Task.Run<object>(() => CalculateContent());
RaisePropertyChanged("Content");
}
public object Content { get; private set; }
...
}
How can I write a Test in NUnit, that checks, that the Content-Property is set to the right value? I want to do something like that:
[Test]
public void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
var viewModel = new EditorViewModel();
viewModel.SetIdentifier("XXX");
Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}
But that doesn't work. Because the asynchronous code has not been executed before the assertion.
Your SetIdentifier method is async too (or you need to make it async because you wait operation inside it. Then your method can looks like next one:
public async Task SetIdentifier(string identifier)
{
await SetContentAsync();
DoSomething();
}
And now you can just await it in your unit test:
[Test]
public async Task Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
var viewModel = new EditorViewModel();
await viewModel.SetIdentifier("XXX");
Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}
You can also use workaround to call your test in a sync manner:
[Test]
public async Task Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
Task.Run(async () =>
{
var viewModel = new EditorViewModel();
await viewModel.SetIdentifier("XXX");
Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}).GetAwaiter().GetResult();
}
Via MSDN Magazine.
When working with async you should always return a Task. Otherwise it would be "fire and forget", and you have absolutely no way of interacting with the Task.
Therefore you should change the signature of SetIdentifier to return a Task, like this:
public async Task SetIdentifier(string identifier)
{
await SetContentAsync();
DoSomething();
}
Then you can wait for the operation to complete in the test:
[Test]
public async void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
var viewModel = new EditorViewModel();
await viewModel.SetIdentifier("XXX");
Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}
Or, if your test runner does not support async:
[Test]
public void Content_WhenModifierIsXXX_ReturnsSomeViewModel()
{
var viewModel = new EditorViewModel();
viewModel.SetIdentifier("XXX").Wait();
Assert.That(viewModel.Content, Is.InstanceOf<ISomeContentViewModel>());
}

Shared resource and async Web API call

I have a simple Web API method that looks like this:
public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
var taskId = await TaskManager.CreateTask(taskType);
TaskManager.Run(taskId);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content =
new StringContent($"Task {taskType.GetDescription()} was started.")
};
}
TaskManager.Run is decalared like this:
public async Task Run(int id)
I was expecting it to return "Task was started" message immediately after TaskManager.Run(taskId) But the request continues to run synchronously.
But if to replace the call TaskManager.Run(taskId) with:
Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(100)));
Then it runs asynchronously.
So I believe this is something to do with the resources shared by TaskManager and main thread. Can a shared resource lock the execution?
I'm using Castle Windsor. One WindsorContainer container is declared in Web API project.
TaskManager utilizes BaseTaskRunner class inside of it. One more WindsorContainer is declared in BaseTaskRunner.
Web API's container uses LifeStyle.PerWebRequest for all components. BaseTaskRunner's container uses LifeStyle.Singleton (not sure if it's correct LifeStyle). Could the call be locked for example by DdContext or other classes declared in both of the containers?
UPD:
I don't want to wait the TaskManager.Run to complete. But what happens is that return statement still waits for the TaskManager.Run to complete (even without await statement on TaskManager.Run).
In other words it does not matter how I call the TaskManager.Run:
TaskManager.Run(taskId);
or
await TaskManager.Run(taskId);
It waits for TaskManager.Run to complete in both cases.
Here is the code of TaskManager:
public class TaskManager : ITaskManager
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
public async Task<int> CreateTask(TaskType type, byte[] data = null, object config = null)
{
var task = new BackgroundTask
{
Type = type,
Status = BackgroundTaskStatus.New,
Config = config?.SerializeToXml(),
Created = DateTime.Now,
Data = data
};
TaskRepository.Add(task);
TaskRepository.SaveChanges();
return task.Id;
}
public async Task Run(int id, bool removeOnComplete = true)
{
var task = TaskRepository.GetById(id);
Run(task, removeOnComplete);
}
public async Task Run(TaskType type, bool removeOnComplete = true)
{
var tasksToRun = TaskRepository.Get(t => t.Type == type);
tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
}
public async Task Run(BackgroundTask task, bool removeOnComplete = true)
{
switch (task.Type)
{
case TaskType.SpreadsheetImport:
new SpreadsheetImportTaskRunner().Run(task);
break;
}
}
}
And some other classes:
public class SpreadsheetImportTaskRunner : BaseTaskRunner
{
public IForecastSpreadsheetManager SpreadsheetManager { get; set; }
protected override void Execute()
{
SpreadsheetManager.ImportActuals(Task.Data);
}
protected override void Initialize()
{
base.Initialize();
SpreadsheetManager = _container.Resolve<IForecastSpreadsheetManager>();
}
}
BaseTaskRunner:
public class BaseTaskRunner
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
protected IWindsorContainer _container = new WindsorContainer();
protected BackgroundTask Task { get; set; }
public async Task Run(BackgroundTask task)
{
Initialize();
Task = task;
try
{
Execute();
}
catch (Exception ex)
{
SetError(ex.ToString());
}
}
protected virtual void Execute()
{
}
protected virtual void Initialize()
{
_container.Install(new TaskRunnerComponentsInstaller());
TaskRepository = _container.Resolve<IRepository<BackgroundTask>>();
}
}
I still believe this is something to do with the WindsorContainer and common classes which are resolved in several different threads.
The issue is that you're not using await on the Task being returned from the invocation of the TaskManager.Run function. Consider the below:
public async Task<HttpResponseMessage> RunTask(TaskType taskType)
{
var taskId = await TaskManager.CreateTask(taskType);
await TaskManager.Run(taskId);
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content =
new StringContent($"Task {taskType.GetDescription()} was started.")
};
}
Now it will work asynchronously as you'd expect. The await sets a continuation marker in the async state-machine, instructing it to return to this portion of the method upon completion of the asynchronous operation defined in the TaskManager.Run.
UPDATE
You are missing lots of await statements, and there are times where you need to not mark methods as async. It appears as though there are some mis-understandings as it pertains to these keywords. Here is what your TaskManager class should look like.
public class TaskManager : ITaskManager
{
public IRepository<BackgroundTask> TaskRepository { get; set; }
public async Task<int> CreateTask(TaskType type,
byte[] data = null,
object config = null)
{
var task = new BackgroundTask
{
Type = type,
Status = BackgroundTaskStatus.New,
Config = config?.SerializeToXml(),
Created = DateTime.Now,
Data = data
};
TaskRepository.Add(task);
TaskRepository.SaveChanges();
return task.Id;
}
public ask Run(int id, bool removeOnComplete = true)
{
var task = TaskRepository.GetById(id);
return Run(task, removeOnComplete);
}
public Task Run(TaskType type, bool removeOnComplete = true)
{
var tasksToRun = TaskRepository.Get(t => t.Type == type);
return tasksToRun.ForEachAsync(t => Run(t, removeOnComplete));
}
public Task Run(BackgroundTask task, bool removeOnComplete = true)
{
switch (task.Type)
{
case TaskType.SpreadsheetImport:
return new SpreadsheetImportTaskRunner().Run(task);
break;
}
}
}
}
Ideally, if the method is marked as a return type of Task and the method doesn't need to unwind any tasks within its execution it can simply return the Task functionality for its implementation. For example, notice how dramatically my TaskManager class differs from yours -- I'm only marking methods as async that need to actually await. These two keywords should be married, if a method uses async there should be an await. But only use await if the method needs to unwind and use the asynchronous operation.

Async method nevers finishes

I have a form, like below, that accepts a Task<T>, waits for completion and then returns after await by closing the form:
public partial class SomeForm<T> : Form
{
public T ReturnValue { get; private set; }
private Task<T> Task { get; set; }
public SomeForm(string waitingText, Task<T> task)
{
InitializeComponent();
...
PerformTask();
}
private async void PerformTask()
{
ReturnValue = await Task;
this.Close();
}
}
However, whilst this method runs happily, it gets to ReturnValue = await Task; and then does not go any further. The method will run normally without being sent to the method and does not delay. I have a feeling it's to do with how I'm using async and await, but I'm new to TPL etc.
Please help me identify what's wrong with the above script, and in identifying why it never returns.
Edit: TaskA was a typo. Here's the Task's method; ImportedFunctions. BS_Robots_LoadDrive(..) is a DllImport of a C++ function, which works just fine synchronously, even on another thread (like in the final snippet), but not with an async paramter.
public uint LoadDisc()
{
uint response = ImportedFunctions.BS_Robots_LoadDrive(DriveLetters[0],
(int)BSRobotConstants.BIN_ID_DEFAULT,
(int)BSRobotConstants.POSITION_TYPE_INPUTBIN,
0);
switch (response)
{
case BSRobotConstants.OK:
case BSRobotConstants.OK_WITH_MESSAGE:
case BSRobotConstants.FROMTRAY_NO_DISC:
case BSRobotConstants.INVALID_DRIVE:
case BSRobotConstants.INVALID_POSITION:
case BSRobotConstants.TOTRAY_NO_DISC:
case BSRobotConstants.TOTRAY_NOT_OPEN:
case BSRobotConstants.FATAL_ERROR:
break;
case BSRobotConstants.BUSY:
break;
case BSRobotConstants.TOTRAY_HAS_DISC:
RejectDisc();
response = LoadDisc();
break;
}
return response;
}
This works:
private async void PerformTask()
{
Task.Start();
Task.Wait();
ReturnValue = Task.Result;
DialogResult = DialogResult.OK;
}
But the first code snippet doesn't.
The task does not continue past ReturnValue = await TaskA; because it is not returning. Verify the Task is running and not getting stuck.
To property utility async-await for object construction use an async factory method.
See: How to initialize an object using async-await pattern
public class Form1<T> : Form
{
public Form1(string waitingText, Task<T> task)
{
Task = Execute(task);
Controls.Add(new Label { Text = waitingText });
}
public T ReturnValue { get { return Task.Result; } }
public Task<T> Task { get; private set; }
private async Task<T> Execute(Task<T> task)
{
var result = await task;
Close();
return result;
}
}
Usage:
var form = new Form1<int>("Hello", Task.Delay(1000).ContinueWith(_ => 1));
form.Show();
var returnValue = await form.Task;
Console.WriteLine(returnValue);

Async methods and progress indicator

I have a silverlight application which is making multiple async calls:
The problem I am facing is to how to determine if all the async calls are finished so that I can stop displaying the progress indicator. In the example below, progress indicator is stopped as soon as the first async method returns.
Any tips on how to resolve this ?
Constructor()
{
startprogressindicator();
callasync1(finished1);
callasync2(finished2);
//.... and so on
}
public void finished1()
{
stopprogressindicator();
}
public void finished2()
{
stopprogressindicator();
}
You need to asynchronously wait for both methods to finish, currently you call stopprogressindicator as soon as any of the method completes.
Refactor your code to return Task from callasync1 and callasync2 Then you can do
var task1 = callasync1();
var task2 = callasync2();
Task.Factory.ContinueWhenAll(new []{task1, task2}, (antecedents) => stopprogressindicator());
I do like the idea of using Task API, but in this case you may simply use a counter:
int _asyncCalls = 0;
Constructor()
{
startprogressindicator();
Interlocked.Increment(ref _asyncCalls);
try
{
// better yet, do Interlocked.Increment(ref _asyncCalls) inside
// each callasyncN
Interlocked.Increment(ref _asyncCalls);
callasync1(finished1);
Interlocked.Increment(ref _asyncCalls);
callasync2(finished2);
//.... and so on
}
finally
{
checkStopProgreessIndicator();
}
}
public checkStopProgreessIndicator()
{
if (Interlocked.Decrement(ref _asyncCalls) == 0)
stopprogressindicator();
}
public void finished1()
{
checkStopProgreessIndicator()
}
public void finished2()
{
checkStopProgreessIndicator()
}

Categories