Execute novamente o teste de unidade .NET com falha do script do PowerShell ou .NET

8

A tarefa de teste do Visual Studio nos Devops do Azure tem um recurso muito interessante: a capacidade de repetir os testes de unidade com falha. Esse é um ótimo recurso quando você tem um longo tempo de teste e alguns testes que são escassos. Esta tarefa de teste no Azure Devops funciona para várias plataformas de teste como xUnit, NUnit e MSTest. (Então, testes escritos para .NET)

insira a descrição da imagem aqui

Seria possível obter o mesmo comportamento do script? Prefiro xUnit ou NUnit e executando o script no PowerShell.

Para xUnit, existe um -method "name":

execute um determinado método de teste (pode ser totalmente especificado ou use um curinga; por exemplo, 'MyNamespace.MyClass.MyTestMethod' ou '* .MyTestMethod') se especificado mais de uma vez, atua como uma operação OR

NUnit tem uma fonte de--where=EXPRESSION sintaxe :

Uma expressão indicando quais testes executar. Pode especificar nomes de teste, classes, métodos, categorias ou propriedades comparando-os aos valores reais com os operadores ==,! =, = ~ E! ~. Consulte Teste de idioma de seleção para obter uma descrição completa da sintaxe.

Mas não sei como coletar o teste com falha para xUnit ou NUnit para fazer tudo funcionar.

Obviamente, corrigir o teste superficial seria melhor, mas às vezes não é tão fácil assim.

Atualização: a partir de. NET / C # (que pode ser acionado no PowerShell) também é aceitável

Julian
fonte
Você pode usar o Pester como estrutura de teste do PowerShell.
Alex_P 20/04
Isso é para testes no PowerShell, não é? Nesse caso, os testes de unidade são escritos em C # /. NET
Julian
Mas não sei como coletar o teste com falha para xUnit ou NUnit para fazer tudo funcionar. Apenas no topo da minha cabeça, talvez use um costume ITestLoggerWithParameterspara criar uma lista de nomes de teste com falha?
weichch 26/04
Então você dotnet test --logger:customcoleciona nomes de teste com falha.
weichch 26/04
Deseja executar apenas os testes com falha do PowerShell ou está correto executar todos os testes pelo PowerShell e executar novamente os testes com falha do PowerShell novamente? Você pode fornecer um exemplo de código mínimo com dois testes (um bem-sucedido e outro com falha)?
Thomas

Respostas:

0

Você pode fazer um pouco de "trabalho manual" para obter o resultado usando expressões regulares no PowerShell.

o exemplo é com XUnit. Então, o que você precisa fazer é armazenar o resultado do dotnet test project.csprojem uma variável. então um exemplo será como o próximo

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 você pode ver, existem alguns padrões comuns que são principalmente Error Message fornecem a dica para saber onde procurar; nesse caso, xUnit indica os erros porX testname [{time}ms] Error Message

se você comparar esse texto com uma expressão regular, poderá obter a resposta desejada: usei esta: X\s*(\S*)\s\[\d*ms\]\s*Error Messagetenho certeza de que pode ser melhorada (não sou mestre em regex), mas faz o seu trabalho. você pode removerError Message por exemplo. de qualquer maneira, eu continuo.

depois de corresponder ao resultado, você só precisa obter o grupo para cada resultado, que neste caso eu o armazenei TestName. e chame odotnet 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 linha $failedTestName = $failedTest.Groups['TestName'].Valueé necessária, se você tentar passar .Groups..oFullyQualifiedName string, o PowerShell as entenderá como uma string literal.

você precisa fazer o mesmo para calcular os tempos e a porcentagem.

Também para a primeira iteração é mais fácil, porque você pode executar todos os testes de uma só vez, mas a partir da segunda e até agora você não pode. lista necessária (para manter os testes que estão falhando) é necessária.

algo assim fará o trabalho.

$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
fonte