0

In program, we have:

var task1 = some task;
task1.start()

If mock the task result in unit test, the result is returned by mock mechanism immediately before calling the start(), and raise "Start may not be called on a task that has completed" exception.

How to address that issue and compose a valid unit test?

Paste a simplified sample code here for clarity, which produces the above exception:

namespace TestTaskStart
{
    public class TestMethods : ITestMethods
    {
        public async Task<int> AlwaysReturnOne(int number)
        {
            return await Task.FromResult(1);
        }
    }
}

namespace TestTaskStart {
    public class TestInvoker
    {
        private ITestMethods testMethods;

        public TestInvoker(ITestMethods testMethods)
        {
            this.testMethods = testMethods;
        }

        public async Task<int> GetANumberWrapperTask(int number)
        {
            // just an exmple of one tasks to be called
            var task = this.testMethods.AlwaysReturnOne(number);
            task.Start();
            Task.WaitAll(task);

            return task.Result;
        }
    }
}

namespace TestTaskStart {
    [TestClass]
    public class UnitTests
    {
        ITestMethods numberGetter;
        TestInvoker testInvoker;

        [TestInitialize]
        public void Setup()
        {
            this.numberGetter = Substitute.For<ITestMethods>();
            this.testInvoker = new TestInvoker(this.numberGetter);
        }

        [TestMethod]
        public void TestGetANumberWrapper()
        {
            this.MockAlwaysReturnOneResult();
            var result = testInvoker.GetANumberWrapperTask(5).Result;
        }

        private void MockAlwaysReturnOneResult()
        {
            this.numberGetter.AlwaysReturnOne(Arg.Any<int>()).Returns(1);
        }
    }
}
Fenton
  • 241,084
  • 71
  • 387
  • 401
Channing
  • 5
  • 3
  • Could you replace the *"some task"* with your actual code? – Theodor Zoulias Apr 09 '22 at 03:40
  • @TheodorZoulias thanks for the comment, append a simplified sample project to the post, have verified it locally with same exception. – Channing Apr 09 '22 at 05:11
  • Somewhat related: [Start may not be called on a promise-style task. exception is coming](https://stackoverflow.com/questions/14230372/start-may-not-be-called-on-a-promise-style-task-exception-is-coming) – Theodor Zoulias Apr 09 '22 at 06:49

1 Answers1

1

The Task.Start method can only be called on "cold" tasks, in other words on tasks that have not been started yet. Such tasks can only be created with the Task constructor. Tasks created by invoking asynchronous methods implemented with the async keyword are "hot", in other words they are already started upon creation. From the docs:

Exceptions
InvalidOperationException
The Task is not in a valid state to be started. It may have already been started, executed, or canceled, or it may have been created in a manner that doesn't support direct scheduling.

This is also a good reading: A Tour of Task, Part 10: Promise Tasks

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104