PetLahev
PetLahev

Reputation: 688

How to run method synchronously from test project (no await)

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

Background

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

Problem

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

Answers (1)

TheRock
TheRock

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

Related Questions