Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moq Raise for an async event listener

in WPF, I am trying to use moq to raise an event that has an async listener hooked to it:

My code:

 public class Provider
    {
       private IService _service
       public Provider(IService service)
       {
         _service = service;
         _service.OnResultsChanged += ChangeResults;
       }

       private async void ChangeResults(List resultsAdded)
       {
         await Task.Run(() => HasResults = true;)
       }
   }

For simplification, i've set hasresults to true, but I am really adding items to a list and as this operation could take a while, I am doing it in a new task. In my test, the following fails, as it doesn't wait for the DoSomething execution:

My test:

[Test]
public void Test()
{
//Arrange
var serviceMock = new Mock<IService>();
var systemUnderTest = new Provider(serviceMock .Object);

//Act
serviceMock.Raise(mock => mock.OnResultsChanged += null);

//Assert
Assert.IsTrue(systemUnderTest.HasResults);
}

is it possible to tell mock.raise to await the execution of my event listener?

Thanks!

like image 522
Tulio Alcantara Avatar asked Dec 13 '17 03:12

Tulio Alcantara


1 Answers

You can convert the test to be async as well and await a delayed task to allow the async event handler to perform its functionality.

The following example uses a delay in the even handler to simulate a potential long running task.

public interface IService {
    event EventHandler OnResultsChanged;
}

public class Provider {
    private IService _service;
    public Provider(IService service) {
        _service = service;
        _service.OnResultsChanged += ChangeResults;
    }

    private async void ChangeResults(object sender, EventArgs e) {
        await Task.Delay(200); //<-- simulate delay
        await Task.Run(() => HasResults = true);
    }

    public bool HasResults { get; set; }
}

By converting the test to async and waiting for the raised event, the assertion was able to be asserted.

[TestClass]
public class MyTestClass {
    [TestMethod]
    public async Task Test() {
        //Arrange
        var serviceMock = new Mock<IService>();
        var systemUnderTest = new Provider(serviceMock.Object) {
            HasResults = false
        };

        //Act
        serviceMock.Raise(mock => mock.OnResultsChanged += null, EventArgs.Empty);

        await Task.Delay(300); //<--wait

        //Assert
        Assert.IsTrue(systemUnderTest.HasResults);
    }
}
like image 67
Nkosi Avatar answered Oct 12 '22 23:10

Nkosi