Reputation: 688
I'm pretty new in using Task and the whole concept around it. Also I'm using Net 4.0 (VSTO development for Office 2007)
I'd like to understand one thing I wrote in my app that is working in production but writing unit test for it ended up with using Thread.Sleep
and checking if the task run to completion or not
The production code is pretty simple, it calls a service to check out an environment status (PROD, STAGE, QA etc.) it's done in a (void
) method via Task
, inside the Task body it sets a property that is updating UI (WPF) and it's working OK. note I'm not using Wait
in that method. If the Task fails I continue with another one with the OnlyOnFaulted
I try to unit test the method (void) so I'd like to run the method running synchronously and I'm failing there.
I read about RunSynchronously, also saw people using task.Result
(which doesn't seems to be available for 4.0), also tried Scheduler.Current
, AttachedToParent
etc.
I think the problem is my design, I guess I'm trying to call a method and saying "Hey, run synchronously" and it does but inside the method is another task that run async so no matter what it will always run async. Am I right? Is there a way how to test it without a loop in my test project and waiting for RunToCompletion
?
Here is sample code I created for better understanding, I simplified it a bit. In real implementation there is a proper Test project in my solution, I'm mocking the service so it doesn't call the real one etc.
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
ImagineThisIsInTestProject();
Console.ReadLine();
Console.WriteLine("Started");
var cls = new Program();
cls.MyProdMethod();
Console.WriteLine("Finished");
Console.ReadLine();
}
public string Status { get; private set; }
public void MyProdMethod()
{
// I have more logic in my real method, serialization, handling errors
Console.WriteLine("In my method");
var task = Task.Factory.StartNew(() => {
Thread.Sleep(2000);
Console.WriteLine("Setting status");
Status = "Yes I set it";
}, CancellationToken.None);
Console.WriteLine("Finishing my method");
}
public static void ImagineThisIsInTestProject()
{
Console.WriteLine("TEST started");
var sut = new Program();
var task = new Task(() => sut.MyProdMethod());
task.RunSynchronously(); // I would assume this will wait until the task is finished?
// here my assert if status is set
Console.WriteLine($"Status is - {sut.Status}");
Console.WriteLine("TEST finished");
}
}
}
Output of the above is
TEST started
In my method
Finishing my method
Status is -
TEST finished
Setting status
Started
In my method
Finishing my method
Finished
Setting status
Upvotes: 0
Views: 1384
Reputation: 1541
You cannot really test your method public void MyProdMethod()
because:
var task = Task.Factory.StartNew(() => {
Thread.Sleep(2000);
Console.WriteLine("Setting status");
Status = "Yes I set it";
}, CancellationToken.None);
is basically a fire and forget
There's no way to test this unless you wait for the task to finish. I know you don't want to include the Wait but to test consider the following:
public void MyProdMethod()
{
// I have more logic in my real method, serialization, handling errors
Console.WriteLine("In my method");
Task.Factory.StartNew(() => {
Thread.Sleep(2000);
Console.WriteLine("Setting status");
Status = "Yes I set it";
}, CancellationToken.None).Wait();
Console.WriteLine("Finishing my method");
}
And then your test would be like the following:
[TestClass]
public class TestClass
{
[TestMethod]
public void Test()
{
// Arrange
var program = new Program();
// Act
program.MyProdMethod();
// Assert
Assert.AreEqual("Yes I set it", program.Status);
}
}
If you take the Wait out the test will finish before the Task in the method has a chance to complete therefore resulting in a failed Assert.
Upvotes: 2