Упражнение. Модульное тестирование функции Azure

Завершено

Модульное тестирование является важной частью гибкой методики. Visual Studio предоставляет шаблон тестового проекта. Этот шаблон используется для создания модульных тестов для приложений, и ту же методику можно применить к тестированию для функций Azure.

В случае веб-сайта по продаже элитных часов у ваших разработчиков существует политика достижения по крайней мере 80 % покрытия кода в модульных тестах. Вы бы хотели реализовать ту же политику для функций Azure.

Здесь вы узнаете, как использовать платформу xUnit тестирования с Visual Studio для тестирования Функции Azure.

Создание проекта модульного теста

Для начала нужно создать проект, содержащий модульные тесты, и добавить его к решению, которое содержит приложение-функцию Azure. Следуйте инструкциям ниже, чтобы создать проект модульного теста для тестирования функции WatchInfo.

  1. В окне Обозревателя решений Visual Studio щелкните правой кнопкой мыши решение WatchPortalFunction, выберите Добавить, а затем — Создать проект.

    Снимок экрана обозревателя решений с командой добавления в решение нового проекта.

  2. В окне Добавить новый проект прокрутите вниз, выберите шаблон значка Тестовый проект xUnit C#+, а затем Далее.

    Снимок экрана: окно

  3. Откроется окно Настройка нового проекта. В поле Имя проекта введите WatchFunctionsTests. Рядом с полем Расположение нажмите на значок обзора и выберите папку WatchPortalFunction.

  4. Выберите Далее. Откроется окно Дополнительные сведения.

  5. В разделе Требуемая версия .NET Framework. примите значение по умолчанию .NET 6.0 (долгосрочная поддержка).

  6. Нажмите кнопку создания.

  7. При добавлении проекта щелкните правой кнопкой мыши проект WatchFunctionTests в окне Обозреватель решений и выберите пункт "Управление пакетами NuGet".

  8. В окне NuGet: WatchFunctionTests выберите вкладку "Обзор". В поле поиска введите Microsoft.AspNetCore.Mvc. Щелкните пакет Microsoft.AspNetCore.Mvc и выберите Установить.

    Снимок экрана: окно диспетчер пакетов NuGet. Пользователь устанавливает пакет Microsoft.AspNetCore.Mvc.

    Примечание.

    Тестовый проект создаст макет среды HTTP. Классы, необходимые для этого, находятся в пакете Microsoft.AspNetCore.Mvc.

  9. Подождите, пока установится пакет. Если появится сообщение Просмотр изменений, щелкните ОК. В диалоговом окне Принятие условий лицензионного соглашения нажмите Я принимаю.

  10. После добавления пакета в окне Обозреватель решений в проекте WatchFunctionsTests щелкните правой кнопкой мыши файл UnitTest1.cs и нажмите кнопку "Переименовать". Измените имя файла на WatchFunctionUnitTests.cs. В появившемся диалоговом окне щелкните Да, чтобы переименовать все ссылки с UnitTest1 на WatchFunctionUnitTests.

  11. В окне Обозревателя решений в проекте WatchFunctionsTest щелкните правой кнопкой мыши Зависимости и нажмите кнопку Добавить ссылку на проект.

  12. В окне диспетчера ссылок выберите проект WatchPortalFunction и нажмите кнопку ОК.

Добавление модульных тестов для функции WatchInfo

Теперь можно добавить модульные тесты в тестовый проект. В сценарии элитных часов вы хотите убедиться, что функция WatchInfo всегда возвращает ответ ОК, если в строке запроса предоставляется модель, и ответ об ошибке, если строка запроса пуста или не содержит параметр model.

Чтобы проверить это поведение, добавьте пару тестов фактов в WatchFunctionsTests.

  1. В окне Обозреватель решений дважды щелкните файл WatchFunctionUnitTests.cs, чтобы открыть его в окне кода.

  2. Добавьте в список в верхней части файла следующие директивы using.

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Http.Internal;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    using Microsoft.Extensions.Logging.Abstractions;
    
  3. Измените имя метода Test1 на TestWatchFunctionSuccess.

  4. В теле метода TestWatchFunctionSuccess добавьте следующий код. Эта инструкция создает макет контекста HTTP и HTTP-запрос. Запрос включает строку запроса, которая содержит параметр model со значением abc.

    var queryStringValue = "abc";
    var request = new DefaultHttpRequest(new DefaultHttpContext())
    {
        Query = new QueryCollection
        (
            new System.Collections.Generic.Dictionary<string, StringValues>()
            {
                { "model", queryStringValue }
            }
        )
    };
    
  5. Добавьте следующие операторы к методу. Эта инструкция создает фиктивное средство ведения журнала.

    var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
    
  6. Добавьте в метод следующий код. Эти инструкции вызывают функцию WatchInfo, передавая фиктивный запрос и средство ведения журнала в качестве параметров.

    var response = WatchPortalFunction.WatchInfo.Run(request, logger);
    response.Wait();
    
  7. Добавьте в метод следующий код. Этот код проверяет правильность ответа функции. В этом случае функция должна возвращать ответ ОК, содержащий ожидаемые данные.

    // Check that the response is an "OK" response
    Assert.IsAssignableFrom<OkObjectResult>(response.Result);
    
    // Check that the contents of the response are the expected contents
    var result = (OkObjectResult)response.Result;
    dynamic watchinfo = new { Manufacturer = "abc", CaseType = "Solid", Bezel = "Titanium", Dial = "Roman", CaseFinish = "Silver", Jewels = 15 };
    string watchInfo = $"Watch Details: {watchinfo.Manufacturer}, {watchinfo.CaseType}, {watchinfo.Bezel}, {watchinfo.Dial}, {watchinfo.CaseFinish}, {watchinfo.Jewels}";
    Assert.Equal(watchInfo, result.Value);
    

    Готовый метод будет выглядеть, как показано ниже.

    [Fact]
    public void TestWatchFunctionSuccess()
    {
        var queryStringValue = "abc";
        var request = new DefaultHttpRequest(new DefaultHttpContext())
        {
            Query = new QueryCollection
            (
                new System.Collections.Generic.Dictionary<string, StringValues>()
                {
                    { "model", queryStringValue }
                }
            )
        };
    
        var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
    
        var response = WatchPortalFunction.WatchInfo.Run(request, logger);
        response.Wait();
    
        // Check that the response is an "OK" response
        Assert.IsAssignableFrom<OkObjectResult>(response.Result);
    
        // Check that the contents of the response are the expected contents
        var result = (OkObjectResult)response.Result;
        dynamic watchinfo = new { Manufacturer = "abc", CaseType = "Solid", Bezel = "Titanium", Dial = "Roman", CaseFinish = "Silver", Jewels = 15 };
        string watchInfo = $"Watch Details: {watchinfo.Manufacturer}, {watchinfo.CaseType}, {watchinfo.Bezel}, {watchinfo.Dial}, {watchinfo.CaseFinish}, {watchinfo.Jewels}";
        Assert.Equal(watchInfo, result.Value);
    }
    
  8. Добавьте еще два метода с именем TestWatchFunctionFailureNoQueryString и TestWatchFunctionFailureNoModel. TestWatchFunctionFailureNoQueryString проверяет, что функция WatchInfo завершается корректно, если не получает строку запроса. TestWatchFunctionFailureNoModel проверяет тот же сбой, если функции передается строка запроса, которая не содержит параметр модели.

    [Fact]
    public void TestWatchFunctionFailureNoQueryString()
    {
        var request = new DefaultHttpRequest(new DefaultHttpContext());
        var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
    
        var response = WatchPortalFunction.WatchInfo.Run(request, logger);
        response.Wait();
    
        // Check that the response is an "Bad" response
        Assert.IsAssignableFrom<BadRequestObjectResult>(response.Result);
    
        // Check that the contents of the response are the expected contents
        var result = (BadRequestObjectResult)response.Result;
        Assert.Equal("Please provide a watch model in the query string", result.Value);
    }
    
    [Fact]
    public void TestWatchFunctionFailureNoModel()
    {
        var queryStringValue = "abc";
        var request = new DefaultHttpRequest(new DefaultHttpContext())
        {
            Query = new QueryCollection
            (
                new System.Collections.Generic.Dictionary<string, StringValues>()
                {
                    { "not-model", queryStringValue }
                }
            )
        };
    
        var logger = NullLoggerFactory.Instance.CreateLogger("Null Logger");
    
        var response = WatchPortalFunction.WatchInfo.Run(request, logger);
        response.Wait();
    
        // Check that the response is an "Bad" response
        Assert.IsAssignableFrom<BadRequestObjectResult>(response.Result);
    
        // Check that the contents of the response are the expected contents
        var result = (BadRequestObjectResult)response.Result;
        Assert.Equal("Please provide a watch model in the query string", result.Value);
    }
    

Запуск тестов

  1. В верхней строке меню в разделе Тест выберите Выполнить все тесты.

    Снимок экрана: меню

  2. В окне обозревателя тестов все три теста должны завершиться успешно.

    Снимок экрана: окно Team Explorer. Все три теста успешно выполнялись.

  3. В окне обозревателя решений в проекте WatchPortalFunction дважды щелкните WatchInfo.cs, чтобы открыть файл в редакторе кода.

  4. Найдите следующий код.

    // Retrieve the model id from the query string
    string model = req.Query["model"];
    
  5. Измените инструкцию, которая задает переменную model, следующим образом. Это изменение имитирует ошибку в коде, допущенную разработчиком.

    string model = req.Query["modelll"];
    
  6. В верхней строке меню в разделе Тест выберите Выполнить все тесты. На этот раз тест TestWatchFunctionSuccess должен завершиться ошибкой. Эта ошибка возникает, так как функция WatchInfo не находит параметр, названный modelll в строке запроса, и поэтому функция возвращает неправильный ответ.

    Снимок экрана: окно Team Explorer. Сбой теста TestWatchFunctionSuccess.

В этом уроке вы узнали, как создать проект модульного теста и реализовать модульные тесты для функции Azure.