2012-10-30 18 views
12

Setup: ho una classe che estende l'IRetryAnalyzer e hanno attuato una logica di tentativi semplice sovrascrivendo il seguente metodo: public boolean tentativi (ITestResult risultato) {TestNG riprovare fallito i test non emette i risultati del test corretto

Ho un'altra classe che estende la classe TestListenerAdapter che riprova i test non riusciti fino a quando non superano o segnalano errori. Ho implementato la mia logica sovrascrivendo il seguente metodo: pubblico onTestFailure void (ITestResult risultato) {

Scenario: ho un totale di 10 prove. 1 su 10 test fallisce 2 volte e passa il terzo tentativo con la mia logica di riprovare. I risultati del test mostrano quanto segue: Test totali: 12, Non riuscito: 2, Salto: 0

Quello che mi piacerebbe è produrre il numero corretto di test eseguiti. E anche ignorare i 2 fallimenti da quando il test è passato alla fine. Quindi il risultato dovrebbe essere simile a questo: Test totali: 10, Non riuscito: 0, Saltato: 0

Cosa mi manca qui? Devo modificare l'oggetto ITestResult? Se sì, come?

NOTA: Sono riuscito a ottenere ciò utilizzando JUnit (implementando l'interfaccia TestRule).

Grazie in anticipo.

+1

Avete trovato una soluzione a questo ancora? Grazie. – derrdji

+0

Purtroppo no. Vivere con il problema per ora. –

risposta

5

Si prega di considerare i seguenti risultati del test con max. 2 tentativi:

  1. superato => nel complesso ok!
  2. Fallito, superato => Nel complesso ok!
  3. Failed, Failed, Passed => Nel complesso ok!
  4. Failed, Failed, Failed => Errore generale!

Quello che ho fatto è creare un listener TestNg che estenda il comportamento predefinito e faccia un po 'di magia dopo che tutti i test sono terminati.

public class FixRetryListener extends TestListenerAdapter { 

    @Override 
    public void onFinish(ITestContext testContext) { 
     super.onFinish(testContext); 

     // List of test results which we will delete later 
     List<ITestResult> testsToBeRemoved = new ArrayList<>(); 

     // collect all id's from passed test 
     Set <Integer> passedTestIds = new HashSet<>(); 
     for (ITestResult passedTest : testContext.getPassedTests().getAllResults()) { 
      passedTestIds.add(TestUtil.getId(passedTest)); 
     } 

     Set <Integer> failedTestIds = new HashSet<>(); 
     for (ITestResult failedTest : testContext.getFailedTests().getAllResults()) { 

      // id = class + method + dataprovider 
      int failedTestId = TestUtil.getId(failedTest); 

      // if we saw this test as a failed test before we mark as to be deleted 
      // or delete this failed test if there is at least one passed version 
      if (failedTestIds.contains(failedTestId) || passedTestIds.contains(failedTestId)) { 
       testsToBeRemoved.add(failedTest); 
      } else { 
       failedTestIds.add(failedTestId); 
      } 
     } 

     // finally delete all tests that are marked 
     for (Iterator<ITestResult> iterator = testContext.getFailedTests().getAllResults().iterator(); iterator.hasNext();) { 
      ITestResult testResult = iterator.next(); 
      if (testsToBeRemoved.contains(testResult)) { 
       iterator.remove(); 
      } 
     } 

    } 

} 

Fondamentalmente io fare 2 cose:

  1. raccogliere tutti i test è stato superato. Se incontro un test fallito con almeno un test superato, rimuovo il test fallito (che copre i casi 2 e 3 dall'alto)
  2. Iterate su tutti i test non riusciti. Se incontro un test fallito che in precedenza non riuscivo, rimuovo il risultato corrente non riuscito. (Ciò riguarderebbe effettivamente il caso 3 e 4). Ciò significa anche che manterrò il primo risultato fallito solo se ci sono molti risultati falliti.

Per identificare un TestResult io uso il seguente semplice funzione hash:

public class TestUtil { 

    public static int getId(ITestResult result) { 
     int id = result.getTestClass().getName().hashCode(); 
     id = 31 * id + result.getMethod().getMethodName().hashCode(); 
     id = 31 * id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0); 
     return id; 
    } 
} 

Questo approccio funziona con dataproviders pure ma ha una piccola limitazione!Se si utilizza dataproviders con valori casuali si incorrerà in problemi:

@DataProvider(name = "dataprovider") 
public Object[][] getData() { 
    return new Object[][]{{System.currentTimeMillis()}}; 
} 

Per i test falliti il ​​dataprovider si rivaluta. Pertanto otterrai un nuovo timestamp e il risultato1 e il risultato2 per lo stesso metodo genereranno diversi valori hash. La soluzione sarebbe includere il parametroIndex nel metodo getId() invece dei parametri, ma sembra che tale valore non sia incluso in ITestResult.

Vedere questo semplice esempio, come prova di concetto:

@Listeners(value = FixRetryListener.class) 
public class SimpleTest { 

    private int count = 0; 

    @DataProvider(name = "dataprovider") 
    public Object[][] getData() { 
     return new Object[][]{{"Run1"},{"Run2"}}; 
    } 

    @Test(retryAnalyzer = RetryAnalyzer.class, dataProvider = "dataprovider") 
    public void teste(String testName) { 
     count++; 
     System.out.println("---------------------------------------"); 
     System.out.println(testName + " " + count); 
     if (count % 3 != 0) { 
      Assert.fail(); 
     } 

     count = 0; 
    } 

} 

produrrà in:

Total tests run: 2, Failures: 0, Skips: 0 
+1

Quando provo questo, in effetti sta rilevando quali test rimuovere in modo corretto (ad esempio stampando i test corretti nel loop iteratore). Tuttavia, ottengo ancora "3 test completati, 2 non riusciti" quando lo eseguo. Non sembra che i risultati vengano effettivamente rimossi. Ti sei imbattuto in questo prima? Sto usando gradle per eseguire i test. – linguinee

+1

Abbiamo lo stesso problema, i test sembrano essere rimossi, ma il risultato rimane invariato. Ho anche provato questo invece di iterator.remove(): testContext.getFailedTests(). RemoveResult (testResult); – mac

+1

Ho notato anche quanto segue: C'è una differenza tra "risultati del test" e "risultati della suite". Suite è corretta ("Suite predefinita - Test totali eseguiti: 2, Errori: 1, Salti: 0") - il primo test fallito è stato rimosso. Tuttavia, test non è ok ("Test predefinito - Esami test: 3, Errori: 2, Salti: 0") - ci sono ancora 3 test in totale e due errori. – mac

0

Io uso questo approccio:

ListenerApadter:

public class MyTestListenerAdapter extends TestListenerAdapter { 
    @Override 
    public void onTestFailure(ITestResult result) { 
     if (result.getMethod().getRetryAnalyzer() != null) { 
      MyRetryAnalyzer retryAnalyzer = (MyRetryAnalyzer)result.getMethod().getRetryAnalyzer(); 

      if(retryAnalyzer.isRetryAvailable()) { 
       result.setStatus(ITestResult.SKIP); 
      } else { 
       result.setStatus(ITestResult.FAILURE); 
      } 
      Reporter.setCurrentTestResult(result); 
     } 
    } 

    @Overrride 
    public void onFinish(ITestContext context) { 
    Iterator<ITestResult> failedTestCases =context.getFailedTests().getAllResults().iterator(); 
    while (failedTestCases.hasNext()) { 
     System.out.println("failedTestCases"); 
     ITestResult failedTestCase = failedTestCases.next(); 
     ITestNGMethod method = failedTestCase.getMethod(); 
     if (context.getFailedTests().getResults(method).size() > 1) { 
      System.out.println("failed test case remove as dup:" + failedTestCase.getTestClass().toString()); 
      failedTestCases.remove(); 
     } else { 

      if (context.getPassedTests().getResults(method).size() > 0) { 
       System.out.println("failed test case remove as pass retry:" + failedTestCase.getTestClass().toString()); 
       failedTestCases.remove(); 
      } 
     } 
    } 
    } 
} 

RetryAnalizer:

public class MyRetryAnalyzer implements IRetryAnalyzer { 
    private static int MAX_RETRY_COUNT = 3; 

    AtomicInteger count = new AtomicInteger(MAX_RETRY_COUNT); 

    public boolean isRetryAvailable() { 
     return (count.intValue() > 0); 
    } 

    @Override 
    public boolean retry(ITestResult result) { 
     boolean retry = false; 
     if (isRetryAvailable()) { 
      System.out.println("Going to retry test case: " + result.getMethod() + ", " + (MAX_RETRY_COUNT - count.intValue() + 1) + " out of " + MAX_RETRY_COUNT); 
      retry = true; 
      count.decrementAndGet(); 
     } 
     return retry; 
    } 
} 

pom.xml -> Configurazione infallibile:

Questo è dove si dovrebbe configurare "sovrascrivere" Quale ascoltatore infallibile ha i propri contatori.

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.18.1</version> 
    <configuration> 
    <suiteXmlFiles><suiteXmlFile>${basedir}/testng.xml</suiteXmlFile></suiteXmlFiles> 
<properties> 
    <property> 
    <name>listener</name> 
    <value>Utils.MyTestListenerAdapter,Utils.MyRetryAnalizer</value> 
    </property> 
</properties> 

1

Il mio approccio si basa su Morvader's answer ma aggiunge la capacità di definire gli analizzatori di tentativi che aderiscono al intento originale della realtà il fallimento della prova anche se il metodo sono passati, dopo alcuni tentativi.

anche io non ho trovato la necessità di tendere al numero di prova nel metodo OnFinish(), i numeri sembravano belle nella versione maven-surefire-plugin 2,18

RetryListenerAdapter

public class RetryListenerAdapter extends TestListenerAdapter { 

    @Override 
    public void onTestFailure(ITestResult tr) { 
     IRetryAnalyzer retryAnalyzer = tr.getMethod().getRetryAnalyzer(); 
     if (retryAnalyzer == null || !(retryAnalyzer instanceof IRetryAnalyzerWithSkip)) { 
      super.onTestFailure(tr); 
     } else if (((IRetryAnalyzerWithSkip) retryAnalyzer).isRetryable()) { 
      tr.setStatus(ITestResult.SKIP); 
      super.onTestSkipped(tr); 
     } else { 
      super.onTestFailure(tr); 
     } 
    } 
} 

IRetryAnalyzerWithSkip

public interface IRetryAnalyzerWithSkip extends IRetryAnalyzer { 
    boolean isRetryable(); 
} 

Riprova

public class Retry implements IRetryAnalyzerWithSkip { 
    private int retryCount = 0; 
    private int maxRetryCount = 3; 

    public boolean retry(ITestResult result) { 

     if (retryCount < maxRetryCount) { 
      retryCount++; 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public boolean isRetryable() { 
     return retryCount < maxRetryCount; 
    } 
} 
2

ho provato, provato e riprovato. Ma ora finalmente ha funzionato

MyRetryAnalyzer.java

import org.testng.IRetryAnalyzer; 
import org.testng.ITestResult; 

import java.util.concurrent.atomic.AtomicInteger; 


public class MyRetryAnalyzer implements IRetryAnalyzer { 
private static int MAX_RETRY_COUNT = 3; 

AtomicInteger count = new AtomicInteger(MAX_RETRY_COUNT); 

public boolean isRetryAvailable() { 
    return (count.intValue() > 0); 
} 

@Override 
public boolean retry(ITestResult result) { 
    boolean retry = false; 
    if (isRetryAvailable()) { 
     System.out.println("Going to retry test case: " + result.getMethod() + ", " + (MAX_RETRY_COUNT - count.intValue() + 1) + " out of " + MAX_RETRY_COUNT); 
     retry = true; 
     count.decrementAndGet(); 
    } 
    return retry; 
} 
} 

MyTestListenerAdapter.java

import org.testng.*; 

import java.util.*; 


public class MyTestListenerAdapter extends TestListenerAdapter { 
    @Override 
    public void onTestFailure(ITestResult result) { 
     if (result.getMethod().getRetryAnalyzer() != null) { 
      MyRetryAnalyzer retryAnalyzer = (MyRetryAnalyzer)result.getMethod().getRetryAnalyzer(); 

      if(retryAnalyzer.isRetryAvailable()) { 
      } else { 
       result.setStatus(ITestResult.FAILURE); 
      } 
      Reporter.setCurrentTestResult(result); 
     } 
    } 

@Override 
    public void onFinish(ITestContext context) { 
     Iterator<ITestResult> failedTestCases =context.getFailedTests().getAllResults().iterator(); 
     while (failedTestCases.hasNext()) { 
      System.out.println("failedTestCases"); 
      ITestResult failedTestCase = failedTestCases.next(); 
      ITestNGMethod method = failedTestCase.getMethod(); 
      if (context.getFailedTests().getResults(method).size() > 1) { 
       System.out.println("failed test case remove as dup:" + failedTestCase.getTestClass().toString()); 
       failedTestCases.remove(); 
      } else { 

       if (context.getPassedTests().getResults(method).size() > 0) { 
        System.out.println("failed test case remove as pass retry:" + failedTestCase.getTestClass().toString()); 
        failedTestCases.remove(); 
       } 
      } 
     } 
    } 
} 

Si classe di test

@Listeners(value = MyTestListenerAdapter.class) 

public class Test { 

//Your data provider 
@DataProvider 

@Test(retryAnalyzer = MyRetryAnalyzer.class) 
public void testMethod() { 
    //your code goes here 
} 

} 
Problemi correlati