Ошибка повторного запуска. NET модульное тестирование из сценария PowerShell или. NET - PullRequest
8 голосов
/ 20 апреля 2020

Задача Visual Studio Test в Azure Devops имеет действительно классную функцию - возможность повторять неудачные юнит-тесты. Это отличная функция, если у вас есть длительное время тестирования и некоторые тесты, которые являются нестабильными. Это тестовое задание в Azure Devops работает для различных тестовых платформ, таких как xUnit, NUnit & MSTest. (Итак, тесты, написанные для. NET)

enter image description here

Возможно ли получить такое же поведение из сценария? Я предпочитаю xUnit или NUnit и запуск сценария в PowerShell.

Для xUnit -method "name":

запустить данный метод тестирования (может быть полностью указан или использовать подстановочный знак; т. Е. 'MyNamespace.MyClass.MyTestMethod' или '* .MyTestMethod '), если указано более одного раза, действует как операция ИЛИ

NUnit имеет синтаксис --where=EXPRESSION source :

Выражение указание, какие тесты для запуска. Он может указывать имена тестов, классы, методы, категории или свойства, сравнивая их с фактическими значениями с помощью операторов ==,! =, = ~ И! ~. См. Язык выбора теста для полного описания синтаксиса.

Но не уверен, как собрать неудачный тест для xUnit или NUnit, чтобы все это заработало.

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

Обновление: выполняется из. NET / C# (который может быть запущен в PowerShell) также приемлемо

1 Ответ

0 голосов
/ 01 мая 2020

Вы можете выполнить небольшую «ручную работу», чтобы получить результат с помощью регулярных выражений в powershell.

В примере используется XUnit. так что вам нужно сохранить результат dotnet test project.csproj в переменной. таким образом, пример будет похож на следующий

Test run for C:\Users\Tigrex\source\repos\ConsoleApp1\XUnitTestProject1\bin\Debug\netcoreapp2.2\XUnitTestProject1.dll(.NETCoreApp,Version=v2.2) Microsoft (R) Test Execution Command Line Tool Version 16.3.0 Copyright (c) Microsoft Corporation. All rights reserved. Starting test execution, please wait... A total of 1 test files matched the specified pattern. X XUnitTestProject1.UnitTest1.ThisIsAnotherFailedTestYesAgain [11ms] Error Message: Assert.Equal() Failure Expected: 2 Actual: 1 Stack Trace: at XUnitTestProject1.UnitTest1.ThisIsAnotherFailedTestYesAgain() in C:\Users\Tigrex\source\repos\ConsoleApp1\XUnitTestProject1\UnitTest1.cs:line 33 X XUnitTestProject1.UnitTest1.ThisIsAnotherFAiledTest [1ms] Error Message: Assert.Equal() Failure Expected: 2 Actual: 1 Stack Trace: at XUnitTestProject1.UnitTest1.ThisIsAnotherFAiledTest() in C:\Users\Tigrex\source\repos\ConsoleApp1\XUnitTestProject1\UnitTest1.cs:line 22 X XUnitTestProject1.UnitTest1.TestToFail [1ms] Error Message: Assert.Equal() Failure Expected: 2 Actual: 1 Stack Trace: at XUnitTestProject1.UnitTest1.TestToFail() in C:\Users\Tigrex\source\repos\ConsoleApp1\XUnitTestProject1\UnitTest1.cs:line 16 Total tests: 5 Passed: 2 Failed: 3 Total time: 1.2764 Seconds

, поскольку вы можете видеть, что есть некоторые общие модели, которые в основном Error Message, которые дают вам подсказку, чтобы знать, где искать, в этом случае xUnit указывает ошибки на X testname [{time}ms] Error Message

, если вы сопоставите этот текст с регулярным выражением, вы можете получить желаемый ответ: я использовал этот: X\s*(\S*)\s\[\d*ms\]\s*Error Message Я уверен, что это может быть улучшенный (я не мастер по регулярным выражениям), но он делает свою работу. вы можете удалить Error Message например. в любом случае, я продолжаю.

как только вы сопоставите результат, вам нужно только получить группу для каждого результата, который в этом случае я сохранил в TestName. и вызовите dotnet test ...

$result = dotnet test XUnitTestProject1/XUnitTestProject1.csproj 

$regex = 'X\s*(?<TestName>\S*)\s\[\d*ms\]\s*'

$matches = [regex]::Matches($result, $regex)
Foreach ($failedTest IN $matches)
{
    $failedTestName = $failedTest.Groups['TestName'].Value
   dotnet test --filter   "FullyQualifiedName=$failedTestName" 
}

, эта строка $failedTestName = $failedTest.Groups['TestName'].Value необходима, если вы попытаетесь передать .Groups.. в строку FullyQualifiedName, PowerShell поймет их как строку литерала.

вам нужно сделать то же самое, чтобы рассчитать время и процент.

Также для первой итерации проще, потому что вы можете запустить все тесты за один go, но со второй и далеко вы не можете. так необходим необходимый список (чтобы сохранить неудачные тесты).

что-то вроде этого сделает работу.

$times = 1

$result = dotnet test XUnitTestProject1/XUnitTestProject1.csproj 
$regexFailedtests = 'X\s*(?<TestName>\S*)\s\[\d*ms\]\s*'
$FailedTestMatches = [regex]::Matches($result, $regexFailedtests)

$totalTestExecutedRegex = 'Total tests:\s*(?<TotalTest>\d*)'
$totalTests = [regex]::Matches($result, $totalTestExecutedRegex)[0].Groups['TotalTest'].Value -as [int]

$totalTesPassedRegex = 'Passed:\s*(?<Passed>\d*)'
$totalTestsPassed = [regex]::Matches($result, $totalTesPassedRegex)[0].Groups['Passed'].Value -as [int]


#convert the failed test into a list of string, so it can be looped.
$listFailedTest = New-Object Collections.Generic.List[string]
Foreach ($failedTest IN $FailedTestMatches)
{
    $failedTestName = $failedTest.Groups['TestName'].Value
    $listFailedTest.Add($failedTestName)
}

$percentage = ($totalTestsPassed*100)/$totalTests #Calculate the percentage

while($times -lt 5 -and $percentage -lt 70) {#5 loops or > 70% of test working

    $listFailedTestInsideDo = New-Object Collections.Generic.List[string]
    $listFailedTestInsideDo = $listFailedTest;  #do a copy of the main list
    $listFailedTest = New-Object Collections.Generic.List[string] ##empty the main list.
    Foreach ($failedTestName IN $listFailedTestInsideDo)
    {

       $result2 = dotnet test --filter   "FullyQualifiedName=$failedTestName" 

       if($result2 -match'Passed:\s*\d*') #if contains passed then it worked
       {
            totalTestsPassed++

        }else{
        $listFailedTest.Add($failedTestName) #add in new List for the new loop
       }
    }

    $percentage = ($totalTestsPassed*100)/$totalTests

    $times++
}
...