JUnit 3では、現在実行中のテストの名前を次のように取得できます。
public class MyTest extends TestCase
{
public void testSomething()
{
System.out.println("Current test is " + getName());
...
}
}
「現在のテストはtestSomething」と出力されます。
JUnit 4でこれを行うためのすぐに使用できる方法または簡単な方法はありますか?
背景:当然、テストの名前だけを印刷したくありません。テストと同じ名前のリソースに保存されているテスト固有のデータをロードしたい。ご存知のとおり、 convention over configuration およびその他すべて。
JUnit 4.7は、この機能を追加しました TestName-Rule を使用しているようです。メソッド名は次のようになります:
import org.junit.Rule;
public class NameRuleTest {
@Rule public TestName name = new TestName();
@Test public void testA() {
assertEquals("testA", name.getMethodName());
}
@Test public void testB() {
assertEquals("testB", name.getMethodName());
}
}
JUnit 4.9以降、 TestWatchman
クラスは非推奨となり、 TestWatcher
クラスが呼び出されます。
@Rule
public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
System.out.println("Starting test: " + description.getMethodName());
}
};
注:収容クラスはpublic
として宣言する必要があります。
次のアプローチは、クラス内のすべてのテストのメソッド名を出力します。
@Rule
public MethodRule watchman = new TestWatchman() {
public void starting(FrameworkMethod method) {
System.out.println("Starting test: " + method.getName());
}
};
JUnit 5では、TestInfo
を挿入できます。これにより、テストメソッドに提供するテストメタデータが簡素化されます。例えば:
@Test
@DisplayName("This is my test")
@Tag("It is my tag")
void test1(TestInfo testInfo) {
assertEquals("This is my test", testInfo.getDisplayName());
assertTrue(testInfo.getTags().contains("It is my tag"));
}
詳細: JUnit 5ユーザーガイド 、 TestInfo javadoc 。
SLF4J(Simple Logging Facade for Java)を使用することを検討してください。パラメーター化されたメッセージを使用して、いくつかのきちんとした改善を提供します。 SLF4JとJUnit 4ルールの実装を組み合わせることで、より効率的なテストクラスロギングテクニックを提供できます。
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.junit.rules.TestWatchman;
import org.junit.runners.model.FrameworkMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoggingTest {
@Rule public MethodRule watchman = new TestWatchman() {
public void starting(FrameworkMethod method) {
logger.info("{} being run...", method.getName());
}
};
final Logger logger =
LoggerFactory.getLogger(LoggingTest.class);
@Test
public void testA() {
}
@Test
public void testB() {
}
}
代わりにこれを試してください:
public class MyTest {
@Rule
public TestName testName = new TestName();
@Rule
public TestWatcher testWatcher = new TestWatcher() {
@Override
protected void starting(final Description description) {
String methodName = description.getMethodName();
String className = description.getClassName();
className = className.substring(className.lastIndexOf('.') + 1);
System.err.println("Starting JUnit-test: " + className + " " + methodName);
}
};
@Test
public void testA() {
assertEquals("testA", testName.getMethodName());
}
@Test
public void testB() {
assertEquals("testB", testName.getMethodName());
}
}
出力は次のようになります。
Starting JUnit-test: MyTest testA
Starting JUnit-test: MyTest testB
注:このDOES NOTは、テストがTestCase!のサブクラスである場合に機能します。テストは実行されますが、@ Ruleコードは実行されません。
複雑な方法は、org.junit.runners.BlockJUnit4ClassRunnerをサブクラス化して独自のランナーを作成することです。
その後、次のようなことができます。
public class NameAwareRunner extends BlockJUnit4ClassRunner {
public NameAwareRunner(Class<?> aClass) throws InitializationError {
super(aClass);
}
@Override
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
System.err.println(frameworkMethod.getName());
return super.methodBlock(frameworkMethod);
}
}
次に、テストクラスごとに、@ RunWith(NameAwareRunner.class)アノテーションを追加する必要があります。あるいは、毎回それを覚えておきたくない場合は、そのアノテーションをTestスーパークラスに置くことができます。もちろん、これはランナーの選択を制限しますが、それは受け入れられるかもしれません。
また、現在のテスト名をランナーからフレームワークに取得するには少しカンフーがかかる場合がありますが、少なくともこれで名前が取得できます。
前のコメントに基づいて、さらに検討して、JUnitテストメソッドでこれを使用できるTestWatherの拡張機能を作成しました。
public class ImportUtilsTest {
private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class);
@Rule
public TestWatcher testWatcher = new JUnitHelper(LOGGER);
@Test
public test1(){
...
}
}
次はテストヘルパークラスです。
public class JUnitHelper extends TestWatcher {
private Logger LOGGER;
public JUnitHelper(Logger LOGGER) {
this.LOGGER = LOGGER;
}
@Override
protected void starting(final Description description) {
LOGGER.info("STARTED " + description.getMethodName());
}
@Override
protected void succeeded(Description description) {
LOGGER.info("SUCCESSFUL " + description.getMethodName());
}
@Override
protected void failed(Throwable e, Description description) {
LOGGER.error("FAILURE " + description.getMethodName());
}
}
楽しい!
JUnit 4には、テストケースが独自の名前を取得するためのすぐに使用できるメカニズムはありません(セットアップ中および分解中を含む)。
String testName = null;
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
for (int i = trace.length - 1; i > 0; --i) {
StackTraceElement ste = trace[i];
try {
Class<?> cls = Class.forName(ste.getClassName());
Method method = cls.getDeclaredMethod(ste.getMethodName());
Test annotation = method.getAnnotation(Test.class);
if (annotation != null) {
testName = ste.getClassName() + "." + ste.getMethodName();
break;
}
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
}
}
@ClassRule
public static TestRule watchman = new TestWatcher() {
@Override
protected void starting( final Description description ) {
String mN = description.getMethodName();
if ( mN == null ) {
mN = "setUpBeforeClass..";
}
final String s = StringTools.toString( "starting..JUnit-Test: %s.%s", description.getClassName(), mN );
System.err.println( s );
}
};
JUnit 5では、 TestInfo
は、JUnit 4のTestNameルールのドロップイン置換として機能します。
ドキュメントから:
TestInfoは、現在のテストまたはコンテナに関する情報を@ Test、@ RepeatedTest、@ ParameterizedTest、@ TestFactory、@ BeforeEach、@ AfterEach、@ BeforeAll、および@AfterAllメソッドに注入するために使用されます。
現在実行されているテストのメソッド名を取得するには、String TestInfo.getDisplayName()
とMethod TestInfo.getTestMethod()
の2つのオプションがあります。
現在のテストメソッドの名前のみを取得するには、テストメソッドのデフォルトの表示名はTestInfo.getDisplayName()
であるため、methodName(TypeArg1, TypeArg2, ... TypeArg3)
では不十分な場合があります。@DisplayName("..")
でメソッド名を重複させる必要はありません。
別の方法として、Optional<Method>
オブジェクトを返すTestInfo.getTestMethod()
を使用できます。
検索メソッドがテストメソッド内で使用される場合、Optional
ラップされた値をテストする必要さえありません。
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.Test;
@Test
void doThat(TestInfo testInfo) throws Exception {
Assertions.assertEquals("doThat(TestInfo)",testInfo.getDisplayName());
Assertions.assertEquals("doThat",testInfo.getTestMethod().get().getName());
}
Slf4j
とTestWatcher
を使用してこれを実現できます
private static Logger _log = LoggerFactory.getLogger(SampleTest.class.getName());
@Rule
public TestWatcher watchman = new TestWatcher() {
@Override
public void starting(final Description method) {
_log.info("being run..." + method.getMethodName());
}
};
テストデータセットからテストメソッド名を分離することをお勧めします。リソースからテストデータのセットをロード/キャッシュするDataLoaderFactoryクラスをモデル化し、テストケースでテストケースのテストデータのセットを返すインターフェイスメソッドを呼び出します。テストデータをテストメソッド名に関連付けると、テストデータは1回しか使用できないと想定されます。ほとんどの場合、同じテストデータを複数のテストで使用してビジネスロジックのさまざまな側面を検証することをお勧めします。