Упражнение. Исправление ошибок по результатам тестирования

Завершено

На данном этапе вы можете выполнять модульные тесты по мере внесения изменений в рамках конвейера сборки. Кроме того, вы можете определять объем кода, проверенного с использованием тестов.

Прежде чем отправлять изменения в конвейер, всегда рекомендуется проводить локальное тестирование. Что произойдет, если кто-то забудет об этом и отправит изменение, которое нарушит работу всей сборки?

В этом уроке вы исправите сломанную сборку, вызванную неудачным модульным тестом. Вам предстоит сделать следующее:

  • Получите начальный код из GitHub.
  • Добавьте средства покрытия кода в проект.
  • Отправить код в собственный репозиторий.
  • Ознакомиться с результатами автоматического тестирования в конвейере и увидеть, что модульные тесты завершились неудачно.
  • Воспроизвести ошибку локально.
  • Проанализировать и исправить ошибку.
  • Отправить исправление и убедиться в успешном выполнении сборки.

Проверка новых модульных тестов

Последняя функция команды включает в себя список лидеров. Нам нужно получить количество показателей из базы данных, чтобы можно было написать модульный тест для проверки IDocumentDBRepository<T>.GetItemsAsync метода.

Этот тест будет выглядеть следующим образом. Добавлять код на данном этапе не нужно.

[TestCase(0, ExpectedResult=0)]
[TestCase(1, ExpectedResult=1)]
[TestCase(10, ExpectedResult=10)]
public int ReturnRequestedCount(int count)
{
    const int PAGE = 0; // take the first page of results

    // Fetch the scores.
    Task<IEnumerable<Score>> scoresTask = _scoreRepository.GetItemsAsync(
        score => true, // return all scores
        score => 1, // we don't care about the order
        PAGE,
        count // fetch this number of results
    );
    IEnumerable<Score> scores = scoresTask.Result;

    // Verify that we received the specified number of items.
    return scores.Count();
}

Напомним, что встроенные данные, используемые для тестирования этого метода, предоставляются в тесте NUnit TestCase. NUnit вызывает метод модульного теста ReturnRequestedCount следующим образом:

ReturnRequestedCount(0);
ReturnRequestedCount(1);
ReturnRequestedCount(10);

Кроме того, в этом тесте используется свойство ExpectedResult, которое позволяет упростить код теста и четко определить его предназначение. NUnit автоматически сравнивает возвращаемое значение со значением этого свойства, что избавляет от необходимости явно вызывать утверждение.

Мы выберем несколько значений, представляющих типичные запросы. Мы также будем включать 0, чтобы покрыть этот пограничный случай.

Извлечение ветви из GitHub

Как и ранее, извлеките failed-test ветвь из GitHub и проверка (или переключитесь на) эту ветвь.

  1. В Visual Studio Code откройте интегрированный терминал.

  2. Выполните следующие git fetch команды и команды, чтобы скачать ветвь failed-test из репозитория Майкрософт и git checkout переключиться на нее:

    git fetch upstream failed-test
    git checkout -B failed-test upstream/failed-test
    

    Мы назвали ветвь failed-test для целей обучения. На практике вы назовете ветвь после ее назначения или функции.

  3. Выполните следующие команды, чтобы создать файл манифеста локального ReportGenerator инструмента, установить средство и добавить coverlet.msbuild пакет в проект тестов:

    dotnet new tool-manifest
    dotnet tool install dotnet-reportgenerator-globaltool
    dotnet add Tailspin.SpaceGame.Web.Tests package coverlet.msbuild
    

    Этот шаг необходим, так как ветвь failed-test не содержит действий, добавленных в ветвь unit-tests.

  4. Добавьте файл тестового проекта и файл манифеста средства в промежуточный индекс и зафиксируйте изменения.

    git add Tailspin.SpaceGame.Web.Tests/Tailspin.SpaceGame.Web.Tests.csproj
    git add .config/dotnet-tools.json
    git commit -m "Configure code coverage tests"
    
  5. Выполните следующую git push команду, чтобы отправить ветвь в репозиторий failed-test GitHub:

    git push origin failed-test
    

Просмотр неудачно завершенного теста в конвейере

Предположим, что вы были в спешке и подтолкнули свою работу, не выполняя тесты один последний раз. К счастью, конвейер может помочь вам поймать проблемы рано, когда есть модульные тесты. Вы можете увидеть это здесь.

  1. В Azure Pipelines трассировка сборки выполняется по конвейеру.

  2. Разверните выполняемую задачу Выполнение модульных тестов — выпуск.

    Обратите внимание, что метод теста ReturnRequestedCount завершился неудачно.

    A screenshot of Azure Pipelines dashboard showing output log of an assertion failure on the unit test, expecting 10 but was 9.

    Тест проходит, когда входное значение равно 0, но происходит сбой, если входное значение равно 1 или 10.

    Сборка публикуется в конвейере только в случае успешного завершения предыдущей задачи. Здесь сборка не была опубликована из-за сбоя модульных тестов. Таким образом, исключается вероятность того, что кто-либо сможет использовать содержащую ошибки сборку.

На практике ручная трассировка сборки во время выполнения осуществляется не всегда. Ниже приведено несколько способов обнаружения сбоя.

  • Уведомление по электронной почте от Azure DevOps

    Вы можете настроить Azure DevOps для отправки уведомления по электронной почте после завершения сборки. Если сборка завершилась неудачно, в строке темы этого сообщения будет указано "[Ошибка сборки]".

    A screenshot of a portion of a build failed email notification.

  • Azure Test Plans

    В Azure DevOps выберите "Тестовые планы" и выберите "Запуски". Появится список недавно запущенных тестов, в том числе и последнего выполненного вами. Выберите последний выполненный тест. Вы видите, что два из восьми тестов завершилось ошибкой.

    A screenshot of Azure DevOps test run outcome showing two of eight failed tests as a ring chart.

  • Панель мониторинга

    В Azure DevOps выберите "Обзор" и выберите "Панели мониторинга". Обратите внимание, что в мини-приложении Тренд по результатам тестирования отображается ошибка. Мини-приложение Объем протестированного кода будет пустым, указывая на то, что тестирование не было завершено.

    A screenshot of Azure DevOps dashboard trend chart widget showing two failed test in the last test run.

  • Значок сборки

    Несмотря на то что в ветви failed-test значок сборки не включен в файл README.md, в случае неудачной сборки в GitHub будет показано следующее:

    A screenshot of Azure Pipelines build badge on GitHub indicating a failure.

Анализ неудачно завершенного теста

При сбое модульных тестов обычно имеется два варианта в зависимости от характера сбоя:

  • Если тест обнаруживает дефект в коде, исправьте код и повторно запустите тесты.
  • Если функциональные возможности изменяются, измените тест на соответствие новым требованиям.

Локальное воспроизведение ошибки

В этом разделе вы будете воспроизводить сбой локально.

  1. В Visual Studio Code откройте интегрированный терминал.

  2. В терминале выполните следующую dotnet build команду, чтобы создать приложение:

    dotnet build --configuration Release
    
  3. В терминале выполните следующую dotnet test команду, чтобы выполнить модульные тесты:

    dotnet test --no-build --configuration Release
    

    В конвейере должны появиться те же ошибки, что и в конвейере. Ниже приведена часть выходных данных:

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
      Failed ReturnRequestedCount(1) [33 ms]
      Error Message:
         Expected: 1
      But was:  0
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
      Failed ReturnRequestedCount(10) [1 ms]
      Error Message:
         Expected: 10
      But was:  9
    
      Stack Trace:
         at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context)
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.<>c__DisplayClass1_0.<Execute>b__0()
       at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(TestExecutionContext context, Action action)
    
    
    Failed!  - Failed:     2, Passed:     6, Skipped:     0, Total:     8, Duration: 98 ms
    

Поиск причин ошибки

Обратите внимание, что каждый неудачный тест создает результат, отключаемый по одному. Например, вместо 10 тест возвращает 9.

Ознакомьтесь с исходным кодом для проверяемого LocalDocumentDBRepository<T>.GetItemsAsyncметода. Вы увидите следующее:

public Task<IEnumerable<T>> GetItemsAsync(
    Func<T, bool> queryPredicate,
    Func<T, int> orderDescendingPredicate,
    int page = 1, int pageSize = 10
)
{
    var result = _items
        .Where(queryPredicate) // filter
        .OrderByDescending(orderDescendingPredicate) // sort
        .Skip(page * pageSize) // find page
        .Take(pageSize - 1); // take items

    return Task<IEnumerable<T>>.FromResult(result);
}

В этом сценарии вы можете проверка GitHub, чтобы узнать, был ли файл недавно изменен.

A screenshot of GitHub showing a file diff where a minus one operation was added.

Вы подозреваете, что pageSize - 1 возвращается один меньший результат, и что это должно быть просто pageSize. В нашем сценарии это ошибка при отправке работы без тестирования, но в реальном сценарии вы можете проверка с разработчиком, который изменил файл на GitHub, чтобы определить причину изменения.

Совет

В GitHub также поддерживаются возможности для обсуждения и совместной работы. Вы можете оставить комментарии к запросу на вытягивание или открыть новый вопрос.

Исправление ошибки

В этом разделе вы исправите ошибку, изменив код обратно в исходное состояние и выполнив тесты для проверки исправления.

  1. В Visual Studio Code откройте Tailspin.SpaceGame.Web/LocalDocumentDBRepository.cs из проводника.

  2. Измените GetItemsAsync метод, как показано здесь:

    public Task<IEnumerable<T>> GetItemsAsync(
        Func<T, bool> queryPredicate,
        Func<T, int> orderDescendingPredicate,
        int page = 1, int pageSize = 10
    )
    {
        var result = _items
            .Where(queryPredicate) // filter
            .OrderByDescending(orderDescendingPredicate) // sort
            .Skip(page * pageSize) // find page
            .Take(pageSize); // take items
    
        return Task<IEnumerable<T>>.FromResult(result);
    }
    

    В этой версии вместо pageSize - 1 используется pageSize.

  3. Сохраните файл.

  4. В интегрированном терминале создайте приложение.

    dotnet build --configuration Release
    

    Вы увидите, что сборка выполнена успешно.

    На практике вы можете запустить приложение и кратко попробовать его. В целях обучения мы пропустим это сейчас.

  5. В терминале выполните модульные тесты.

    dotnet test --no-build --configuration Release
    

    Обратите внимание, что тесты завершаются успешно.

    Starting test execution, please wait...
    A total of 1 test files matched the specified pattern.
    
    Passed!  - Failed:     0, Passed:     8, Skipped:     0, Total:     8, Duration: 69 ms
    
  6. В интегрированном терминале добавьте каждый измененный файл в индекс, зафиксируйте изменения и отправьте ветвь до GitHub.

    git add .
    git commit -m "Return correct number of items"
    git push origin failed-test
    

    Совет

    Точка (.) в этом git add примере является диким карта символом. Он соответствует всем файлам в текущем каталоге и его подкаталогах, для которых не было выполнено промежуточное сохранение.

    Прежде чем использовать этот дикий карта символ, рекомендуется выполняться git status перед фиксацией, чтобы убедиться, что вы выполняете промежуточное выполнение файлов, которые планируется выполнить.

  7. Вернитесь в Azure Pipelines. Просмотрите, как изменение распространяется по конвейеру. Тесты проходят, и общая сборка завершается успешно.

    При необходимости, чтобы проверить результаты теста, можно выбрать вкладки "Тесты и покрытие кода" после завершения сборки.

    Вы также можете проверка панели мониторинга для просмотра обновленной тенденции результатов.

    A screenshot of Azure DevOps dashboard trend chart widget showing a return to all tests passing.

Отлично! Вы исправили сборку. Далее вы узнаете, как очистить среду Azure DevOps.