2013-03-05 8 views
21

Sto lavorando a un progetto in cui è necessario richiamare TestNG in modo programmatico (utilizzando i provider di dati). Le cose vanno bene tranne che nel report, stiamo ricevendo il nome del metodo @Test, che è generico per gestire molti casi. Quello che vorremmo è ottenere un nome significativo nel rapporto.Nome del metodo di prova personalizzato nei report TestNG

Stavo facendo ricerche su questo e ho trovato 3 modi, ma sfortunatamente, tutti stanno fallendo per me.

1) Attuare ITest

ho trovato su questo here e here

io pongo il nome che voglio, non appena inserisco il metodo @Test (Per tutti i 3 modi che ho provato, questo è come sto impostando il nome). Questo nome viene restituito da getTestName(). Quello che ho osservato è che getTestName() viene chiamato prima e dopo il mio @Test. Inizialmente, restituisce null (per la gestione di NullPointerException, restituisco "" invece di null) e successivamente restituisce il valore corretto. Ma io non vedere questo sempre riflessa nella relazione

Edit: provato anche l'impostazione del nome da @ BeforeMethod come suggerito da artdanil

2 e 3

Entrambi si basano su soluzioni di cui al il second link above

con l'override setName in XmlSuite, sto ottenendo

Exception in thread "main" java.lang.AssertionError: l should not be null 
     at org.testng.ClassMethodMap.removeAndCheckIfLast(ClassMethodMap.java:58) 
     at org.testng.internal.TestMethodWorker.invokeAfterClassMethods(TestMethodWorker.java:208) 
     at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:114) 
     at org.testng.TestRunner.privateRun(TestRunner.java:767) 
     ... 

Con l'override toString(), vedo questi in tronchi (con i miei commenti), ma non gli aggiornamenti in rapporto

[2013-03-05 14:53:22,174] (Main.java:30) - calling execute 
    [2013-03-05 14:53:22,346] GenericFunctionTest.<init>(GenericFunctionTest.java:52) - inside constructor 
    [2013-03-05 14:53:22,372] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning **//this followed by 3 invocations before arriving at @Test method** 
    [2013-03-05 14:53:22,410] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,416] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,455] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning 
    [2013-03-05 14:53:22,892] GenericFunctionTest.<init>(GenericFunctionTest.java:52) - inside constructor 
    [2013-03-05 14:53:23,178] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning **//again blank as i havent set it yet** 
    [2013-03-05 14:53:23,182] GenericFunctionTest.getResult(GenericFunctionTest.java:69) - inside with test case:TestCase{signature=Signature{...}}**//I am setting it immedietely after this** 
    [2013-03-05 14:53:23,293] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning MyMethodName **//What i want** 
    [2013-03-05 14:53:23,299] GenericFunctionTest.toString(GenericFunctionTest.java:276) - returning MyMethodName **// again** 

Edit: provato di nuovo tutti e 3 da hardcoding un valore piuttosto che impostare su ingresso di il mio metodo di prova Ma gli stessi risultati

+0

Lo faccio utilizzando un rapporto ascoltatore TestNG per innescare una classe che costruisce il mio HTML fuori il risultato iSuite. Per visualizzare dati arbitrari nel report di test, come i valori degli argomenti del test, penso che dovresti aggiungere dati a ITestContext per ogni test in modo che lo scrittore di report abbia accesso ai dati aggiuntivi.Fortunatamente il nome del metodo fa già parte di quel contesto e puoi recuperarlo. – djangofan

risposta

15

Ho avuto lo stesso problema e ho scoperto che è utile impostare il campo in cui memorizzare il nome del test case nel metodo annotato con @BeforeMethod, usando native injection of TestNG per fornire il nome del metodo ei parametri di test. Il nome del test è preso dai parametri di prova forniti dallo DataProvider. Se il tuo metodo di prova non ha parametri, basta segnalare il nome del metodo.

//oversimplified for demontration purposes 
public class TestParameters { 
    private String testName = null; 
    private String testDescription = null; 

    public TestParameters(String name, 
          String description) { 
     this.testName = name; 
     this.testDescription = description; 
    } 

    public String getTestName() { 
     return testName; 
    } 
    public String getTestDescription() { 
     return testDescription; 
    } 
} 

public class SampleTest implements ITest { 
    // Has to be set to prevent NullPointerException from reporters 
    protected String mTestCaseName = ""; 

    @DataProvider(name="BasicDataProvider") 
    public Object[][] getTestData() { 
     Object[][] data = new Object[][] { 
       { new TestParameters("TestCase1", "Sample test 1")}, 
       { new TestParameters("TestCase2", "Sample test 2")}, 
       { new TestParameters("TestCase3", "Sample test 3")}, 
       { new TestParameters("TestCase4", "Sample test 4")}, 
       { new TestParameters("TestCase5", "Sample test 5") } 
     }; 
     return data; 
    } 

    @BeforeMethod(alwaysRun = true) 
    public void testData(Method method, Object[] testData) { 
     String testCase = ""; 
     if (testData != null && testData.length > 0) { 
      TestParameters testParams = null; 
      //Check if test method has actually received required parameters 
      for (Object testParameter : testData) { 
       if (testParameter instanceof TestParameters) { 
        testParams = (TestParameters)testParameter; 
        break; 
       } 
      } 
      if (testParams != null) { 
       testCase = testParams.getTestName(); 
      } 
     } 
     this.mTestCaseName = String.format("%s(%s)", method.getName(), testCase); 
    } 

    @Override 
    public String getTestName() { 
     return this.mTestCaseName; 
    } 

    @Test(dataProvider="BasicDataProvider") 
    public void testSample1(TestParameters testParams){ 
     //test code here 
    } 

    @Test(dataProvider="BasicDataProvider") 
    public void testSample2(TestParameters testParams){ 
     //test code here 
    } 

    @Test 
    public void testSample3(){ 
     //test code here 
    } 
} 

EDIT: Sulla base dei commenti qui sotto, ho realizzato un campione dalla relazione sarà utile.

Estratto dal rapporto di esecuzione codice precedente:

<testng-results skipped="0" failed="0" total="5" passed="5"> 
    <suite name="SampleTests" duration-ms="2818" started-at="<some-time>" finished-at="<some-time>"> 
    <test name="Test1" duration-ms="2818" started-at="<some-time>" finished-at="<some-time>"> 
     <test-method 
      status="PASS" 
      signature="testSample1(org.example.test.TestParameters)[pri:0, instance:[email protected]]" 
      test-instance-name="testSample1(TestCase5)" 
      name="testSample1" 
      duration-ms="1014" 
      started-at="<some-time-before>" 
      data-provider="BasicDataProvider" 
      finished-at="<some-time-later>" > 
      <!-- excluded for demonstration purposes --> 
     </test-method> 
     <!-- the rest of test results excluded for brevity --> 
    </test> 
    </suite> 
</testng-result> 

nota, che il valore restituito da getTestName() metodo è l'attributo , e non l'attributo name.

+0

+1 per la risposta. Ho controllato questo e sono in grado di vedere il nome previsto nei log sia in @BeforeMethod che in getTestName(). Ma non vedo questo riflesso nei rapporti. C'è qualcos'altro da fare? Qualche configurazione cambia? My DataProvider è una classe separata. Inoltre non sto creando una classe TestParameters. Sto estraendo il nome richiesto dal mio oggetto TestCase. Spero che questi non creino problemi. ** Come ho detto nel mio qn, anche un nome hardcoding in getTestName() non si rifletteva **. Qualsiasi aiuto è gradito – rajesh

+0

Quale rapporto stai guardando? Se si sta verificando il report XML TestNG, è necessario cercare l'attributo 'test-instance-name'. – artdanil

+0

Stavo controllando i rapporti HTML. Sì in XML, il nome-istanza-test sta arrivando correttamente. Ma gli utenti stanno controllando i report HTML. Qualche idea se questo può riflettersi laggiù? – rajesh

1

Provare a implementare l'interfaccia org.testng.ITest che richiede un metodo getTestName(). In questo modo i report gestiscono correttamente il valore restituito.

+0

Questo è stato già fatto e spiegato nella mia domanda – rajesh

2

Mi sono imbattuto in un problema simile. Prima ho implementato la strategia ITest già menzionata. E questo è parte della soluzione, ma non completamente.

TestNG, per qualche motivo, quando si creano report diversi, chiamate getName() sul test durante la creazione del report. Questo va bene se non si utilizza un fornitore di dati per generare esecuzioni diverse e impostare un nome univoco per ogni esecuzione utilizzando la strategia ITest. Se si utilizza un fornitore di dati per generare più esecuzioni dello stesso test e si desidera che ogni esecuzione abbia un nome univoco, si verifica un problema. Come la strategia ITest lascia il nome per il test come il nome impostato dall'ultima esecuzione.

Quindi ho dovuto implementare un getName() molto personalizzato. Alcune ipotesi (nel mio caso particolare):

  1. Solo tre i rapporti vengono eseguiti: TestHTMLReporter, EmailableReporter, XMLSuiteResultWriter.
  2. Quando mai il nome non viene chiamato come risultato di uno dei presunti reporter, restituire il nome correntemente impostato va bene.
  3. Quando un reporter è in esecuzione, effettua le sue chiamate getName() in ordine e solo 1 volta per ogni esecuzione.
public String getTestName() 
{ 
    String name = testName; 
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();//.toString(); 
    if(calledFrom(stackTrace, "XMLSuiteResultWriter")) 
    { 
     name = testNames.size()>0?testNames.get(xmlNameIndex<testNames.size()?xmlNameIndex:0):"undefined"; 
     xmlNameIndex++; 
     if(xmlNameIndex>=testNames.size()) 
      xmlNameIndex = 0; 
    } 
    else if(calledFrom(stackTrace, "EmailableReporter")) 
    { 
     name = testNames.size()>0?testNames.get(emailNameIndex<testNames.size()?emailNameIndex:0):"undefined"; 
     emailNameIndex++; 
     if(emailNameIndex>=testNames.size()) 
      emailNameIndex = 0; 
    } 
    if(calledFrom(stackTrace, "TestHTMLReporter")) 
    { 
     if(testNames.size()<0) 
     { 
      name = "undefined"; 
     } 
     else 
     { 
      if(htmlNameIndex < testNamesFailed.size()) 
      { 
       name = testNamesFailed.get(htmlNameIndex); 
      } 
      else 
      { 
       int htmlPassedIndex = htmlNameIndex - testNamesFailed.size(); 
       if(htmlPassedIndex < testNamesPassed.size()) 
       { 
        name = testNamesPassed.get(htmlPassedIndex); 
       } 
       else 
       { 
        name = "undefined"; 
       } 
      } 
     } 
     htmlNameIndex++; 
     if(htmlNameIndex>=testNames.size()) 
      htmlNameIndex = 0; 
    } 
    return name; 
} 

private boolean calledFrom(StackTraceElement[] stackTrace, String checkForMethod) 
{ 
    boolean calledFrom = false; 
    for(StackTraceElement element : stackTrace) 
    { 
     String stack = element.toString(); 
     if(stack.contains(checkForMethod)) 
      calledFrom = true; 
    } 
    return calledFrom; 
} 

Quando si imposta il nome per la corsa (ho fatto questo nel @BeforeMethod (AlwaysRun metodo = true) ho definito seguendo la strategia ITest) ho aggiunto il nome a un ArrayList testNames. Ma poi il rapporto html non era corretto. La maggior parte degli altri report richiama le informazioni in ordine, come XMLSuiteResultWriter, ma TestHTMLReporter ottiene il nome recuperando prima tutti i nomi per i test non riusciti e quindi i nomi per il superamento dei test. Quindi ho dovuto implementare ArrayList aggiuntivi: testNamesFailed e testNamesPassed e aggiungere loro i nomi dei test quando il test è terminato in base al fatto che siano passati o meno.

E ammetto che questo è un vero e proprio e molto fragile. Idealmente, TestNG aggiunge i test a una raccolta durante l'esecuzione e ottiene il nome da quella raccolta anziché dal test originale. Se si dispone di TestNG per eseguire altri report, è necessario determinare l'ordine in cui richiedono i nomi e la stringa abbastanza unica da ricercare nella traccia dello stack thread.

--edit 1

In alternativa, utilizzare la strategia ITest e il modello di fabbrica (annotazioni @Factory).

TestNG Using @Factory and @DataProvider

http://beust.com/weblog/2004/09/27/testngs-factory/

Si richiede alcune modifiche minori. Ciò include la creazione di un costruttore con gli stessi parametri del metodo di prova originale. Il metodo di prova ora non ha parametri. È possibile impostare il nome nel nuovo costruttore e restituirlo semplicemente nel metodo getTestName. Assicurati di rimuovere le specifiche del fornitore di dati dal metodo di prova.

2

Se si desidera modificare il nome nel report HTML, sarà un attacco totale. Ecco come ho fatto:

public class NinjaTest { 
... 
... 
@AfterMethod (alwaysRun = true) 
public void afterMethod(ITestResult result, Method method) { 
    try { 
     //I have XML test suites organized in directories. 
     String xmlFile = result.getTestContext().getCurrentXmlTest().getSuite().getFileName(); 
     String suiteName = xmlFile.substring(xmlFile.lastIndexOf("\\") + 1, xmlFile.lastIndexOf(".xml")); 
     String pathToFile = xmlFile.substring(0, xmlFile.lastIndexOf("\\")); 
     String directory = pathToFile.substring(pathToFile.lastIndexOf("\\") + 1); 
     String testMethodName = String.format("%s/%s - %s", directory, suiteName, method.getName()); 

     //Total hack to change display name in HTML report \(^o^)/ 
     Field methodName = org.testng.internal.BaseTestMethod.class.getDeclaredField("m_methodName"); 
     methodName.setAccessible(true); 
     methodName.set(result.getMethod(), testMethodName); 
    } catch (Exception e) { 
     // Eh.... ¯\_(ツ)_/¯ 
     e.printStackTrace(); 
    } 
} 
... 
...