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>());
}
Related
I'm curious is it possible to execute function Function1 and function Function2 in C# webapi project like in example below.
Both of these functions are on the same class and use async and await but return different types.
Example code below:
[ApiController]
[Route("[controller]")]
public class ClassController : ControllerBase
{
// ...
public async Task<ActionResult<string>> Test()
{
var message = await _myClass.Function1().Function2();
return Ok(message);
}
// ...
}
Declaration of _myClass looks like below:
public class MyClass
{
// ...
public async Task<MyClass> Function1()
{
// code which uses `await` below
// ....
// end of this code
return this;
}
public async Task<string> Function2()
{
// code which uses `await` below
// ....
// end of this code
return "some text";
}
}
Yes, you can do this:
public async Task<ActionResult<string>> Test()
{
var message = await
_myClass.Function1().ContinueWith(resultingMyClass =>
resultingMyClass.Result.Function2());
return Ok(message);
}
But the most obvious solution would be to use the async/await syntax that C# provides:
public async Task<ActionResult<string>> Test()
{
var result1 = await _myClass.Function1();
var message = await result1.Function2();
return Ok(message);
}
If you don't need the result from the first call as input to the second function you can run multiple Tasks in parallel and wait until they have all finished using Task.WhenAll.
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();
}
In my unit test, I'm trying to mock out the Run() async method from my interface IPipeline and simulate a delay, where it is called in the class PipelineScheduler
public interface IPipeline
{
Task Run();
}
Test Moq:
[SetUp]
public void SetUp()
{
_mockPipeline = new Mock<IPipeline>();
_mockPipeline.Setup(x => x.Run()).Returns(async () =>
{
await Task.Delay(3000);
});
_scheduler = new PipelineScheduler(_mockPipeline.Object);
}
However when I run the test and debug where the mocked method is called
await _pipeline.Run().ConfigureAwait(false);
there is no delay and execution continues after this line immediately.
But if I replace the mock with a stub class, the delay works fine.
private class MockPipeline : IPipeline
{
public async Task Run()
{
await Task.Delay(3000);
}
}
[SetUp]
public void SetUp()
{
_mockPipeline = new MockPipeline();
_scheduler = new PipelineScheduler(_mockPipeline);
}
So I suppose the question is what's different from how I'm creating the delay with moq vs my stubbed class?
The difference is that the setup is being configured incorrectly.
Returning a Task i.e:.Returns(Task.Delay(3000)); is all that is needed for the the setup to behave a desired. The previous setup is a fire and forget async void which is why the previous example did not wait and continued immediately.
The following minimal example demonstrates how the mock should have been setup
[TestClass]
public class MyTestClass {
[TestMethod]
public async Task MyTestMethod() {
//Arrange
var _mockPipeline = new Mock<IPipeline>();
_mockPipeline.Setup(x => x.Run()).Returns(Task.Delay(3000)).Verifiable();
var sut = new PipelineScheduler(_mockPipeline.Object);
//Act
await sut.MethodUnderTest();
//Assert
_mockPipeline.Verify();
}
}
public interface IPipeline {
Task Run();
}
public class PipelineScheduler {
private IPipeline _pipeline;
public PipelineScheduler(IPipeline pipeline) {
this._pipeline = pipeline;
}
public async Task MethodUnderTest() {
await _pipeline.Run().ConfigureAwait(false);
}
}
When exercised, the test delays for the configured 3 seconds.
An old question but I just had the problem and the correct way is not the accepted answer. The correct way is to use a function in the return.
i.e.
.Returns(() => Task.Delay(3000))
Without the () =>, the Delay is only applied once, even if you do multiple sequential calls, for example, like if you did this :
await _pipeline.Run().ConfigureAwait(false);
await _pipeline.Run().ConfigureAwait(false);
await _pipeline.Run().ConfigureAwait(false);
See this reproduction on dotnetfiddle.net
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);
}
I create a method in class :
public async void Foo()
{
.....
string response = await Utilities.sendData(data);
....
}
I create break point and run,when it call foo method,but break point run at
string response = await Utilities.sendData(data)
and then break point is disappear,if i call in code behind (xaml)it no problem
You can call an async method from synchronous code.
The async modifier says that the code within that method can await on other async methods. Here's a silly example
public class Foo
{
public void DoSomething()
{
await Something(); //invalid
Something(); //valid
}
public async void Something()
{
await SomethingElse(); //valid
SomethingElse(); // also valid, but synchronous
}
public async void SomethingElse()
{
}
}