Я создал эту функцию Http Azure с помощью SendGrid и не уверен, как написать модульный тест, чтобы вызвать его с другим тестовым примером электронной почты

#c# #unit-testing #azure-functions

#c# #модульное тестирование #azure-функции

Вопрос:

Меня попросили создать эту функцию Http azure ниже. Я пытаюсь написать макет модульного теста для вызова этого processEmail. Я предполагаю, что мой запрос будет точкой входа. Мой модульный тест должен иметь другое значение электронной почты для тестирования. Если бы я мог привести пример из моей функции ниже, это было бы здорово.

    public async Task<IActionResult> ProcessEmail(
        [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
        HttpRequest req, ILogger log) {

        log.SmpLogInformation("Initializing SendGrid ProcessEmail");

        var client =
           new SendGridClient("key");
       
        var requestBody = new StreamReader(req.Body).ReadToEnd();
        var data = JsonConvert.DeserializeObject<EmailContent>(requestBody);

        if(data == null) {
            throw new ArgumentNullException("Can't proced further");
        }
        var message = new SendGridMessage();
        message.AddTo(data.Email);
        message.SetFrom(new EmailAddress("ff.com"));
        message.AddContent("text/html", HttpUtility.HtmlDecode(data.Body));
        message.SetSubject(data.Subject);
        log.SmpLogDebug("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; } = "";
    }
}
 

Комментарии:

1. Как вы собираетесь издеваться над SendGridClient?

Ответ №1:

Во-первых, вам нужно издеваться над своим SendGridClient , иначе вы будете делать фактические запросы во время своих модульных тестов, что было бы не очень хорошо.

Глядя на код SendGrid, SendGridClient реализует интерфейс ISendGridClient . Вместо того, чтобы запускать клиент с помощью var client = new SendGridClient("key"); , вы могли бы использовать внедрение зависимостей для внедрения экземпляра ISendGridClient через конструктор:

 public class ProcessEmail
{
    private readonly ISendGridClient _client;

    public ProcessEmail(ISendGridClient client)
    {
        _client = client;
    }
 

Затем вы можете удалить эту строку:

 var client = new SendGridClient("key");
 

А затем небольшое изменение в этой строке для использования введенного объекта:

 await _client.SendEmailAsync(message);
 

Затем, когда вы приступите к написанию своего модульного теста, вы сможете создать макет для ISendGridClient интерфейса, который позволяет настраивать и проверять поведение объектов. Вот пример использования 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;
    }
}
 

Полный код функции:

 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; } = "";
    }
}