Firstly, you need to mock your SendGridClient
otherwise you will be making actual requests during your unit tests which wouldn't be great.
Looking at the SendGrid code, SendGridClient
implements an interface ISendGridClient
. Instead of new'ing up the client using var client = new SendGridClient("key");
, you could use dependency injection to inject an instance of ISendGridClient
via the constructor:
public class ProcessEmail
{
private readonly ISendGridClient _client;
public ProcessEmail(ISendGridClient client)
{
_client = client;
}
You can then remove this line:
var client = new SendGridClient("key");
And then a slight change to this line to use the injected object:
await _client.SendEmailAsync(message);
Then when you come to write your unit test, you will be able to have a mock for the ISendGridClient
interface which allows you to setup and verify behaviour of objects. Here's an example using Moq:
[TestClass]
public class ProcessEmailTests
{
private readonly Mock<ISendGridClient> _mockSendGridClient = new Mock<ISendGridClient>();
private readonly Mock<ILogger> _mockLogger = new Mock<ILogger>();
private ProcessEmail _processEmail;
private MemoryStream _memoryStream;
[TestInitialize]
public void Initialize()
{
// initialize the ProcessEmail class with a mock object
_processEmail = new ProcessEmail(_mockSendGridClient.Object);
}
[TestMethod]
public async Task GivenEmailContent_WhenProcessEmailRuns_ThenEmailSentViaSendgrid()
{
// arrange - set up the http request which triggers the run method
var expectedEmailContent = new ProcessEmail.EmailContent
{
Subject = "My unit test",
Body = "Woohoo it works",
Email = "unit@test.com"
};
var httpRequest = CreateMockRequest(expectedEmailContent);
// act - call the Run method of the ProcessEmail class
await _processEmail.Run(httpRequest, _mockLogger.Object);
// assert - verify that the message being sent into the client method has the expected values
_mockSendGridClient
.Verify(sg => sg.SendEmailAsync(It.Is<SendGridMessage>(sgm => sgm.Personalizations[0].Tos[0].Email == expectedEmailContent.Email), It.IsAny<CancellationToken>()), Times.Once);
}
private HttpRequest CreateMockRequest(object body = null, Dictionary<string, StringValues> headers = null, Dictionary<string, StringValues> queryStringParams = null, string contentType = null)
{
var mockRequest = new Mock<HttpRequest>();
if (body != null)
{
var json = JsonConvert.SerializeObject(body);
var byteArray = Encoding.ASCII.GetBytes(json);
_memoryStream = new MemoryStream(byteArray);
_memoryStream.Flush();
_memoryStream.Position = 0;
mockRequest.Setup(x => x.Body).Returns(_memoryStream);
}
if (headers != null)
{
mockRequest.Setup(i => i.Headers).Returns(new HeaderDictionary(headers));
}
if (queryStringParams != null)
{
mockRequest.Setup(i => i.Query).Returns(new QueryCollection(queryStringParams));
}
if (contentType != null)
{
mockRequest.Setup(i => i.ContentType).Returns(contentType);
}
mockRequest.Setup(i => i.HttpContext).Returns(new DefaultHttpContext());
return mockRequest.Object;
}
}
Full function code:
public class ProcessEmail
{
private readonly ISendGridClient _client;
public ProcessEmail(ISendGridClient client)
{
_client = client;
}
[FunctionName("ProcessEmail")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("Initializing SendGrid ProcessEmail");
var requestBody = new StreamReader(req.Body).ReadToEnd();
var data = JsonConvert.DeserializeObject<EmailContent>(requestBody);
if (data == null)
{
throw new ArgumentNullException("Can't proceed further");
}
var message = new SendGridMessage();
message.AddTo(data.Email);
message.SetFrom(new EmailAddress("ff.com"));
message.AddContent("text/html", HttpUtility.HtmlDecode(data.Body));
message.Subject = data.Subject;
log.LogDebug("Email sent through Send Grid");
await _client.SendEmailAsync(message);
return (ActionResult)new OkObjectResult("Submited sucessfully");
}
public class EmailContent
{
public string? Email { get; set; }
public string? Subject { get; set; }
public string Body { get; set; } = "";
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…