Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for an asyncrhonous event raised event in a unit test [duplicate]

I am trying to unit test raising of the event SmtpClient.SendCompleted from a unit test, and I am running into an annoying problem with the test continuing to process before the event can actually fire, terminating the application without actually getting to the event. So, imagine the following code:

[TestClass]
public class emailTest
{
    public bool sentEmail = false;

    [TestMethod]
    public void sendEmail()
    {
        SmtpClient smtp = new SmtpClient("smtpserver");
        smtp.SendCompleted += delegate(Object sender, System.ComponentModel.AsyncCompletedEventArgs e) { sentEmail = true; };
        MailMessage mm = new MailMessage("[email protected]", "[email protected]", "test subject", "test body");
        smtp.SendAsync(mm, "test");
        Assert.IsTrue(sentEmail);
    }
}

This test fails, however, if I manually insert a delay like so...

[TestClass]
public class emailTest
{
    public bool sentEmail = false;

    [TestMethod]
    public void sendEmail()
    {
        SmtpClient smtp = new SmtpClient("smtpserver");
        smtp.SendCompleted += delegate(Object sender, System.ComponentModel.AsyncCompletedEventArgs e) { sentEmail = true; };
        MailMessage mm = new MailMessage("[email protected]", "[email protected]", "test subject", "test body");
        smtp.SendAsync(mm, "test");
        System.Threading.Thread.Sleep(50000); // Manual Delay
        Assert.IsTrue(sentEmail);
    }
}

Then the test passes.

having the method await smtp.SendAsync by wrapping it as a task doesn't seem to actually work because I'm not actually waiting on SendAsync, I'm trying to wait for SendCompleted to finish executing before proceeding with the rest of the test, and I'm not quite sure how to do it.

For time reasons, it's very important that I wait only the minimum amount of time for SendCompleted to finish processing.

I did a whole lot of searching and just can't seem to find anything that addresses this specific issue.

quick edit: in all cases, the email successfully sends, it's only the test that fails.

like image 851
user3657661 Avatar asked Aug 26 '15 18:08

user3657661


1 Answers

Well, I must admit that the fact that SendCompleted is only fired after SendAsync returns sounds a bit odd... it does make unit testing harder.

But if you want to wait the minimum amount of time, you'll have to introduce a synchronization primitive. AutoResetEvent sounds like a good fit for this scenario.

// Arrange
var are = new AutoResetEvent(false);

var smtp = new SmtpClient("smtpserver");
smtp.SendCompleted += (s, e) => { are.Set(); };
var mm = new MailMessage("[email protected]", "[email protected]", "test subject", "test body");

// Act
smtp.SendAsync(mm, "test");

// Assert
var wasSignaled = are.WaitOne(timeout: TimeSpan.FromSeconds(1));
Assert.True(wasSignaled);
like image 190
dcastro Avatar answered Oct 19 '22 22:10

dcastro