Testing an Asynchronous Event

I implemented the MVP pattern (MVC) in winforms C #.

My View and Presenter are the following (without all MVP glue):

public interface IExampleView
{
    event EventHandler<EventArgs> SaveClicked;
    string Message {get; set; }
}

public partial class ExampleView : Form
{
    public event EventHandler<EventArgs> SaveClicked;

    string Message { 
        get { return txtMessage.Text; } 
        set { txtMessage.Text = value; } 
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        if (SaveClicked != null) SaveClicked.Invoke(sender, e);
    }
}

public class ExamplePresenter
{
    public void OnLoad()
    {
        View.SaveClicked += View_SaveClicked;
    }

    private async void View_SaveClicked(object sender, EventArgs e)
    {
        await Task.Run(() => 
        {
            // Do save
        });

        View.Message = "Saved!"
    }

I use MSTest for unit testing, and also NSubstitute for ridicule. I want to simulate a button click in a view in order to test the controller code View_SaveClicked, as shown below:

[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
    // Arrange

    // Act
    View.SaveClicked += Raise.EventWith(new object(), new EventArgs());

    // Assert
    Assert.AreEqual("Saved!", View.Message);
}

I can raise View.SaveClickedusing NSubstitute Raise.EventWith. However, the problem is that the code immediately jumps to Assertbefore the Presenter has time to save the message, but Asserta failure.

, , Thread.Sleep(500) Assert, , . , presenter.Save() , , "".

, , unit test, , async View_SaveClicked View/Presenter, .

?

+4
3

, SynchronizationContext, async void.

AsyncContext type :

[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
  // Arrange

  AsyncContext.Run(() =>
  {
    // Act
    View.SaveClicked += Raise.EventWith(new object(), new EventArgs());
  });

  // Assert
  Assert.AreEqual("Saved!", View.Message);
}

async void ( MSDN async). async".

, EventHandler<T> await:

public Func<Object, EventArgs, Task> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
  if (SaveClicked != null) await SaveClicked(sender, e);
}

, :

public delegate Task AsyncEventHandler<T>(object sender, T e);
public event AsyncEventHandler<EventArgs> SaveClicked;
private void btnSave_Click(object sender, EventArgs e)
{
  if (SaveClicked != null)
    await Task.WhenAll(
      SaveClicked.GetInvocationList().Cast<AsyncEventHandler<T>>
          .Select(x => x(sender, e)));
}

Task.CompletedTask .

, EventArgs "". , .

+2
0

, Func :

public interface IExampleView
{
    Func<Object, EventArgs, Task> SaveClicked { get; set; }
    string Message { get; set; }
}

public partial class ExampleView : Form
{
    public Func<Object, EventArgs, Task> SaveClicked { get; set; }

    string Message
    {
        get { return txtMessage.Text; }
        set { txtMessage.Text = value; }
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        if (SaveClicked != null) SaveClicked(sender, e);
    }
}

public class ExamplePresenter
{
    public void OnLoad()
    {
        View.SaveClicked = View_SaveClicked;
    }

    private async Task View_SaveClicked(object sender, EventArgs e)
    {
        await Task.Run(() =>
        {
            // Do save
        });

        View.Message = "Saved!"
    }
}

:

[TestMethod]
public void WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
    // Arrange

    // Act
    View.SaveClicked(new object(), new EventArgs()).Wait();

    // Assert
    Assert.AreEqual("Saved!", View.Message);
}

[TestMethod]
public async Task WhenSaveButtonClicked_ThenSaveMessageShouldBeShown()
{
    // Arrange

    // Act
    await View.SaveClicked(new object(), new EventArgs());

    // Assert
    Assert.AreEqual("Saved!", View.Message);
}
-1

All Articles