Volver a ejecutar la prueba unitaria .NET fallida del script PowerShell o .NET

8

La tarea de prueba de Visual Studio en Azure Devops tiene una característica realmente genial, la capacidad de volver a intentar las pruebas unitarias fallidas. Esta es una gran característica cuando tiene un largo tiempo de prueba y algunas pruebas que son escamosas. Esta tarea de prueba en Azure Devops funciona para varias plataformas de prueba como xUnit, NUnit y MSTest. (Entonces pruebas escritas para .NET)

ingrese la descripción de la imagen aquí

¿Sería posible obtener el mismo comportamiento del script? Prefiero xUnit o NUnit y ejecutar el script en PowerShell.

Para xUnit hay un -method "name":

ejecutar un método de prueba dado (puede especificarse completamente o usar un comodín; es decir, 'MyNamespace.MyClass.MyTestMethod' o '* .MyTestMethod') si se especifica más de una vez, actúa como una operación OR

NUnit tiene una fuente de--where=EXPRESSION sintaxis :

Una expresión que indica qué pruebas ejecutar. Puede especificar nombres de prueba, clases, métodos, categorías o propiedades comparándolos con valores reales con los operadores ==,! =, = ~ Y! ~. Consulte Test Selection Language para obtener una descripción completa de la sintaxis.

Pero no estoy seguro de cómo recopilar la prueba fallida para xUnit o NUnit para que todo funcione.

Por supuesto, arreglar la prueba escamosa sería mejor, pero eso a veces no es tan fácil.

Actualización: desde. NET / C # (que podría activarse en PowerShell) también es aceptable

Julian
fuente
Puede usar Pester como marco de prueba de PowerShell.
Alex_P
Eso es para las pruebas en PowerShell no lo es? Ese no es el caso aquí, las pruebas unitarias están escritas en C # /. NET
Julian
Pero no estoy seguro de cómo recopilar la prueba fallida para xUnit o NUnit para que todo funcione. Justo en la parte superior de mi cabeza, ¿tal vez usar una costumbre ITestLoggerWithParameterspara crear una lista de nombres de prueba fallidos?
weichch
Entonces vas dotnet test --logger:customa recoger nombres de prueba fallidos.
weichch
¿Solo desea ejecutar las pruebas fallidas de PowerShell o está bien ejecutar todas las pruebas de PowerShell y luego volver a ejecutar las pruebas fallidas de PowerShell? ¿Puede proporcionar un ejemplo de código mínimo con dos pruebas (una exitosa y otra fallida)?
Thomas

Respuestas:

0

Puede hacer un poco de "trabajo manual" para obtener el resultado utilizando expresiones regulares en powershell.

El ejemplo es con XUnit. entonces lo que tienes que hacer es almacenar el resultado de dotnet test project.csprojen una variable. entonces un ejemplo será como el siguiente

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

Como puede ver, hay algunos patrones comunes que principalmente le Error Messagedan la pista de saber dónde buscar, en este caso, xUnit indica los errores porX testname [{time}ms] Error Message

si hace coincidir ese texto con una expresión regular, puede obtener la respuesta deseada: utilicé esta: X\s*(\S*)\s\[\d*ms\]\s*Error Messageestoy seguro de que se puede mejorar (no soy un maestro en expresiones regulares ) pero hace su trabajo. puedes eliminar Error Messagepor ejemplo. de todos modos, sigo adelante.

una vez que coincida con el resultado, solo necesita obtener el grupo para cada resultado, que en este caso lo almacené TestName. y llama aldotnet 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" 
}

esta línea $failedTestName = $failedTest.Groups['TestName'].Valuees necesaria, si intenta pasar .Groups..la FullyQualifiedNamecadena, PowerShell las entiende como una cadena literal.

debes hacer lo mismo para calcular los tiempos y el porcentaje.

También para la primera iteración es más fácil porque puedes ejecutar todas las pruebas de una vez, pero desde la segunda y lejos no puedes. es necesaria la lista necesaria (para mantener las pruebas que están fallando).

algo como esto hará el trabajo.

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