web-dev-qa-db-ja.com

失敗した.NET単体テストをPowerShellスクリプトまたは.NETから再実行する

Azure DevopsのVisual Studioテストタスク には、失敗した単体テストを再試行する機能という非常に優れた機能があります。これは、テスト時間が長く、不安定なテストがいくつかある場合に優れた機能です。 Azure Devopsのこのテストタスクは、xUnit、NUnit、MSTestなどのさまざまなテストプラットフォームで機能します。 (したがって、.NET用に書かれたテスト)

enter image description here

スクリプトから同じ動作を取得することは可能ですか?私はxUnitまたはNUnitを好み、PowerShellでスクリプトを実行します。

XUnitの場合、-method "name"

特定のテストメソッドを実行します(完全に指定することも、ワイルドカードを使用することもできます(例: 'MyNamespace.MyClass.MyTestMethod'または '* .MyTestMethod')。2回以上指定した場合、OR操作

NUnitには--where=EXPRESSION構文 ソース

実行するテストを示す式。演算子==、!=、=〜および!〜を使用して、実際の値と比較するテスト名、クラス、メソッド、カテゴリ、またはプロパティを指定できます。構文の詳細については、 テスト選択言語 を参照してください。

しかし、xUnitまたはNUnitの失敗したテストを収集してすべてを機能させる方法はわかりません。

もちろん、薄片状のテストを修正する方が良いでしょうが、それは時々それほど簡単ではありません。

更新:から実行しています。 NET/C#(PowerShellでトリガーされる可能性があります)も許容されます

8
Julian

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_が必要です。FullyQualifiedName文字列で_.Groups.._を渡そうとすると、PowerShellはそれらをリテラル文字列として認識します。

時間とパーセンテージを計算するには、同じことを行う必要があります。

また、最初の反復ではすべてのテストを一度に実行できるため簡単ですが、2回目以降は実行できません。必要なリスト(失敗したテストを維持するため)が必要です。

このようなものは仕事をします。

_$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++
}
_
1
TiGreX