从PowerShell脚本或.NET重新运行失败的.NET单元测试

问题描述 投票:8回答:1

Visual Studio Test task in Azure Devops具有非常酷的功能,可以重试失败的单元测试。当您的测试时间较长且有些测试不稳定时,这是一个很棒的功能。 Azure Devops中的此测试任务适用于各种测试平台,例如xUnit,NUnit和MSTest。 (因此,为.NET编写的测试)

enter image description here

是否有可能从脚本中获得相同的行为?我更喜欢xUnit或NUnit并在PowerShell中运行脚本。

对于xUnit,有一个-method "name"

如果多次指定,则运行给定的测试方法(可以完全指定或使用通配符;即'MyNamespace.MyClass.MyTestMethod'或'* .MyTestMethod'),用作OR操作

NUnit具有--where=EXPRESSION语法source

表示要运行哪些测试的表达式。它可以指定测试名称,类,方法,类别或属性,并使用运算符==,!=,=〜和!〜将它们与实际值进行比较。有关语法的完整说明,请参见Test Selection Language

但不知道如何收集xUnit或NUnit的失败测试以使其全部正常工作。

当然,修复片状测试会更好,但有时并不那么容易。

更新:从开始运行。 NET / C#(可以在PowerShell中触发)也是可以接受的

c# powershell unit-testing nunit xunit.net
1个回答
0
投票

您可以做一些“手工工作”来使用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我敢肯定它可以改进(我不是regex的高手),但它确实可以做到。例如,您可以删除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会将其理解为文字字符串。

您需要执行相同的操作来计算时间和百分比。

第一次迭代也比较容易,因为您可以一次运行所有测试,但是从第二次开始您就无法进行。因此有必要的清单(以保持失败的测试)。

类似这样的工作即可。

$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++
}
© www.soinside.com 2019 - 2024. All rights reserved.