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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With