1

I have a class that depends on TaskCompletionSource

An example of the class looks like this:

public ExampleClass
{
    private TaskCompletionSource<string> _tcs;

    public async Task<string> GetFooAsync()
    {
        _tcs = new TaskCompletionSource<string>();
        return await _tcs.Task;
    }

    public void SetFoo(string value)
    {
        _tcs.SetResult(value);
    }
}

I am using xUnit.net as my testing framework.

[Fact]
public async Task ReturnsString()
{
    // Arrange
    const string value = "test";
    var myclass = new ExampleClass();

    // Act -- Does not work. I don't know how to fix this.
    var result = await myclass.GetFooAsync(); // Won't return before SetFoo is called
    myclass.SetFoo(value); // Needs to be run after GetFooAsync is called

    // Assert
    Assert.Equal(value, result);
}

(see comments in code)

Nkosi
  • 235,767
  • 35
  • 427
  • 472
Fred
  • 12,086
  • 7
  • 60
  • 83
  • Why would you need to? Your example class looks like a wrapper around TaskCompletionSource. Why not just unit test that? (I guess it's part of a library. Then it should already be tested.) What are you trying to achieve? – k0pernikus Oct 07 '19 at 10:53
  • That being said: Instead of initializing the TaskCompletionSource in your method, inject it from the outside, either through constructor or as parameter, and work on that instance. In your test, you can then inject a mock of TaskCompletionSource. As your goal should be to test `ExampleClass`, not `TaskCompletionSource`. – k0pernikus Oct 07 '19 at 10:54
  • @k0pernikus, No, it is not a library, it is an web application. The API of the application would not allow me to interact directly with a `TaskCompletionSource`, so I had to wrap it. @Nkosi helped me get exactly what I wanted. – Fred Oct 07 '19 at 17:48

1 Answers1

1

For this example case, the test needs to be arranged differently

[Fact]
public async Task ReturnsString() {
    // Arrange
    const string expected = "test";
    var sut = new ExampleClass();

    var task = sut.GetFooAsync(); // launch tack and do not await
    sut.SetFoo(expected); // set expected value after GetFooAsync is called

    // Act
    var actual = await task;

    // Assert
    Assert.Equal(expected, actual);
}

The task can be launched and not awaited to allow for the sut to be able to set the task result.

Once the result is set, the task can then be awaited as intended to verify the expected behavior

Nkosi
  • 235,767
  • 35
  • 427
  • 472