TestNGをプログラムで呼び出す必要があるプロジェクトに取り組んでいます(データプロバイダーを使用)。レポートで、@ Testメソッドの名前を取得していることを除いて、問題はありません。これは、多くのケースを処理するための汎用的なメソッドです。私たちが望んでいるのは、レポートで意味のある名前を取得することです。
私はこれについて調査していて、3つの方法を見つけましたが、残念ながら、すべてが失敗しています。
1)ITestを実装する
@Testメソッドに入るとすぐに、必要な名前を設定します(3つの方法すべてについて、これが名前の設定方法です)。この名前はgetTestName()から返されます。私が観察したのは、@ Testの前後にgetTestName()が呼び出されていることです。最初はnullを返し(NullPointerExceptionを処理するため、nullではなく ""を返します)、後で正しい値を返します。しかし、私はこれがレポートに反映されているのを見ません
Edit:artdanilによって提案されているように、@ BeforeMethodから名前を設定することも試みました
2および3
どちらも 上記の2番目のリンク で与えられたソリューションに基づいています
XmlSuiteでsetNameをオーバーライドすることにより、
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)
...
ToString()をオーバーライドすると、これらは(コメントとともに)ログに表示されますが、レポートには更新がありません
[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**
編集:テストメソッドのエントリに値を設定するのではなく、値をハードコーディングすることで3つすべてを再試行しました。しかし同じ結果
同じ問題があり、_@BeforeMethod
_で注釈が付けられたメソッドにテストケース名を格納するフィールドを設定すると、メソッド名とテストパラメーターを提供するために TestNGのネイティブインジェクション を使用するのに役立ちます。テスト名は、DataProvider
によって提供されるテストパラメータから取得されます。テストメソッドにパラメーターがない場合は、メソッド名を報告してください。
_//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
}
}
_
編集:以下のコメントに基づいて、レポートのサンプルが役立つことに気付きました。
上記のコードを実行したレポートから抽出します。
_<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:org.example.test.TimeTest@c9d92c]"
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>
_
getTestName()
メソッドから返される値は_test-instance-name
_属性にあり、name
属性にはないことに注意してください。
TestNGレポートでテストケースのカスタム名を設定するための次のコードを見つけてください。
このコードでは、次の機能を使用できます。
複数のテストケース実行の並列実行を設定する
import Java.lang.reflect.Field;
import org.testng.ITest;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Factory;
import org.testng.annotations.Test;
import org.testng.internal.BaseTestMethod;
import com.test.data.ServiceProcessData;
public class ServiceTest implements ITest {
protected ServiceProcessData serviceProcessData;
protected String testCaseName = "";
@Test
public void executeServiceTest() {
System.out.println(this.serviceProcessData.toString());
}
@Factory(dataProvider = "processDataList")
public RiskServiceTest(ServiceProcessData serviceProcessData) {
this.serviceProcessData = serviceProcessData;
}
@DataProvider(name = "processDataList", parallel = true)
public static Object[] getProcessDataList() {
Object[] serviceProcessDataList = new Object[0];
//Set data in serviceProcessDataList
return serviceProcessDataList;
}
@Override
public String getTestName() {
this.testCaseName = "User custom testcase name";
return this.testCaseName;
}
@AfterMethod(alwaysRun = true)
public void setResultTestName(ITestResult result) {
try {
BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod();
Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName");
f.setAccessible(true);
f.set(baseTestMethod, this.testCaseName);
} catch (Exception e) {
ErrorMessageHelper.getInstance().setErrorMessage(e);
Reporter.log("Exception : " + e.getMessage());
}
}}
ありがとう
HTMLレポートの名前を変更したい場合、それは完全なハックになります。ここに私がそれをした方法があります:
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();
}
}
...
...
私は同様の問題に遭遇しました。最初に、すでに述べたITest戦略を実装しました。そしてこれはソリューションの一部ですが、完全ではありません。
TestNGは、何らかの理由で、異なるレポートを作成するときに、レポートの作成中にテストでgetName()を呼び出します。データプロバイダーを使用して異なる実行を生成せず、ITest戦略を使用して各実行に一意の名前を設定しない場合は、これで問題ありません。データプロバイダーを使用して同じテストの複数の実行を生成し、各実行に一意の名前を付けたい場合は、問題があります。 ITest戦略では、テストの名前を最後の実行で設定された名前のままにします。
そのため、非常にカスタムなgetName()を実装する必要がありました。 SOMEの仮定(私の特定の場合):
- 実行されるレポートは、TestHTMLReporter、EmailableReporter、XMLSuiteResultWriterの3つだけです。
- 想定されるレポーターのいずれかの結果としてget nameが呼び出されない場合は、現在設定されている名前を返すのが適切です。
- レポーターが実行されている場合、レポーターは順番にgetName()を呼び出し、実行ごとに1回だけ実行します。
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;
}
実行の名前を設定するとき(ITest戦略に従って定義した@BeforeMethod(alwaysRun = true)メソッドでこれを行いました)、名前をArrayList testNamesに追加しました。しかし、その後、htmlレポートは正しくありませんでした。他のレポートのほとんどは、XMLSuiteResultWriterのように順番に情報を取得しますが、TestHTMLReporterは、最初に失敗したテストのすべての名前を取得し、次にテストに合格するための名前を取得することで名前を取得します。そのため、追加のArrayListsに実装する必要がありました:testNamesFailedとtestNamesPassedで、合格したかどうかに基づいてテストが終了したときにテスト名を追加しました。
そして、私はこれが非常にハックであり、非常に壊れやすいことを自由に認めます。理想的には、TestNGは実行中にテストをコレクションに追加し、元のテストではなくそのコレクションから名前を取得します。 TestNGで他のレポートを実行する場合は、名前を要求する順序と、スレッドスタックトレースで検索するのに十分な一意の文字列を特定する必要があります。
-編集1
または、ITest Strategyとファクトリパターン(@factoryアノテーション)を使用します。
@ Factoryおよび@DataProviderを使用したTestNG
http://beust.com/weblog/2004/09/27/testngs-factory/
若干の変更が必要です。これには、元のテストメソッドと同じパラメーターを持つコンストラクターの作成が含まれます。テストメソッドにはパラメーターがありません。新しいコンストラクタで名前を設定し、単にgetTestNameメソッドでそれを返すことができます。必ずテストメソッドからデータプロバイダーの仕様を削除してください。
GetTestName()メソッドを必要とするorg.testng.ITestインターフェースを実装してみてください。このようにして、レポートは戻り値を適切に処理します。