2012-10-30 8 views
12

Setup: Mam klasa, która rozszerza IRetryAnalyzer i wdrożyliśmy prostą logikę ponawiania nadrzędnymi następującą metodę: publicznego ponawiania logiczna (ITestResult wynik) {TestNG ponawianie prób nie udało wyjście powoduje prawidłowe badanie

Mam inną klasę, która rozszerza klasę TestListenerAdapter, która ponawia testy, które zakończyły się niepowodzeniem, dopóki nie przejdą lub nie zgłoszą niepowodzeń. I wprowadziły mojego logiki nadrzędnymi następującą metodę: public void onTestFailure (ITestResult wynik) {

Scenariusz: mam łącznie 10 testów. 1 z 10 testów nie powiedzie się 2 razy i trzecią próbę powtórzyć z moją logiką ponownych prób. Wyniki testu są następujące: Testy łącznie: 12, Nie powiodło się: 2, Pominięto: 0

Chciałbym wyprowadzić prawidłową liczbę uruchomionych testów. A także pomiń 2 awarie, ponieważ test zakończył się na końcu. Tak więc wynik powinien wyglądać mniej więcej tak: Razem testy: 10, nie powiodło się: 0, pomijane: 0

Czego tu mi brakuje? Czy muszę zmodyfikować obiekt ITestResult? Jeśli tak, w jaki sposób?

FYI: Udało mi się to osiągnąć za pomocą JUnit (wdrożenie interfejsu TestRule).

Z góry dziękuję.

+1

Znalazłeś rozwiązanie tego jeszcze? Dzięki. – derrdji

+0

Niestety nie. Życie z problemem na razie. –

Odpowiedz

5

Proszę wziąć pod uwagę następujące wyniki testu z max. 2 Ponowna próba:

  1. Udana => Ogólnie ok!
  2. Nie powiodło się, przeszły => Wszystko w porządku!
  3. Nie powiodło się, niepowodzenie, przekazano => Ogólnie ok!
  4. Błąd, niepowodzenie, niepowodzenie => Ogólny błąd!

To, co zrobiłem, to stworzenie detektora TestNg, który rozszerza domyślne zachowanie i robi trochę magii po zakończeniu wszystkich testów.

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(); 
      } 
     } 

    } 

} 

Zasadniczo zrobić 2 rzeczy:

  1. zebrać wszystkie przeszły test. Jeśli napotkam na nieudany test z co najmniej jednym zdanym testem, usuwam nieudany test (który obejmowałby przypadek 2 i 3 z góry)
  2. Powtórzyć test zakończony niepowodzeniem. Jeśli napotkam nieudany test, który poprzednio się nie powiódł, usunę bieżący błąd. (Dotyczy to w rzeczywistości przypadku 3 i 4). Oznacza to również, że zachowam tylko pierwszy nieudany wynik, jeśli jest kilka nieudanych wyników.

Aby zidentyfikować testresult używam następującą prostą funkcję skrótu:

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; 
    } 
} 

To podejście działa z DataProvider'ów jak dobrze, ale ma jeden mały ograniczeń!Jeśli używasz DataProvider'ów z wartościami losowymi będziesz napotkasz problemy:

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

Dla nieudanych testach DataProvider jest reevaluated. Dlatego otrzymasz nowy znacznik czasu, a wynik1 i wynik2 dla tej samej metodzie będą skutkować różnymi wartościami skrótu. Rozwiązaniem byłoby włączenie parametruIndex do metody getId() zamiast parametrów, ale wydaje się, że taka wartość nie jest zawarta w ITestResult.

Zobacz ten prosty przykład jako dowód pojęcia:

@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; 
    } 

} 

przyniesie w:

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

Kiedy próbuję tego, faktycznie wykrywa, które testy należy usunąć poprawnie (tj. Wydrukowanie poprawnych testów w pętli iteratora). Jednak nadal otrzymuję "3 testy zakończone, 2 nie powiodło się", gdy go uruchomię. Wygląda na to, że wyniki nie są usuwane. Czy już to robisz? Używam gradle do wykonania testów. – linguinee

+1

Mamy ten sam problem, testy wydają się być usunięte, ale wynik pozostaje niezmieniony. Próbowałem nawet tego zamiast iterator.remove(): testContext.getFailedTests(). RemoveResult (testResult); – mac

+1

Zauważyłem też, co następuje: Istnieje różnica między "wynikami testu" i "wynikami w pakiecie". Pakiet jest poprawny ("Domyślny pakiet - Łączny przebieg testów: 2, Usterki: 1, Pomiń: 0") - jeden test zakończony niepowodzeniem został usunięty. Jednak test nie jest w porządku ("Test domyślny - Uruchom testy: 3, Usterki: 2, Pomija: 0") - wciąż są 3 testy i dwie awarie. – mac

0

Używam tego podejścia:

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 -> Murowany Konfiguracja:

tym miejscu należy skonfigurować "nadpisać" murowany słuchacza Wich ma własne liczniki.

<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

Moje podejście opiera się na Morvader's answer ale dodaje możliwość definiowania analizatorów ponawiania które przylegają do pierwotnej intencji faktycznie braku testu nawet jeśli metoda minęło po kilku próbach.

ja również nie znaleźliśmy potrzebę tendencję do liczby testów w sposobie onFinish(), numery wydawało się w porządku w wersji Maven-murowany-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(); 
} 

Retry

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

Próbowałem, próbowałem i próbowałem. Ale teraz wreszcie zadziałało

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(); 
       } 
      } 
     } 
    } 
} 

Ty klasa Test

@Listeners(value = MyTestListenerAdapter.class) 

public class Test { 

//Your data provider 
@DataProvider 

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

} 
Powiązane problemy