รับชื่อของการทดสอบที่กำลังดำเนินการใน JUnit 4


240

ใน JUnit 3 ฉันสามารถรับชื่อของการทดสอบที่รันอยู่ในปัจจุบันเช่นนี้:

public class MyTest extends TestCase
{
    public void testSomething()
    {
        System.out.println("Current test is " + getName());
        ...
    }
}

ซึ่งจะพิมพ์ "การทดสอบปัจจุบันคือการทดสอบบางอย่าง"

มีวิธีการนอกกรอบหรือวิธีการง่ายๆใน JUnit 4 หรือไม่?

พื้นหลัง: เห็นได้ชัดว่าฉันไม่ต้องการพิมพ์ชื่อการทดสอบ ฉันต้องการโหลดข้อมูลเฉพาะของการทดสอบที่เก็บไว้ในทรัพยากรที่มีชื่อเดียวกับการทดสอบ คุณรู้ว่าการประชุมเกี่ยวกับการกำหนดค่าและทุกสิ่งที่


รหัสข้างต้นให้อะไรคุณใน JUnit 4
Bill the Lizard

5
การทดสอบ JUnit 3 ขยาย TestCase โดยที่ getName () ถูกกำหนดไว้ การทดสอบ JUnit 4 ไม่ขยายคลาสพื้นฐานดังนั้นจึงไม่มีเมธอด getName () เลย
Dave Ray

ฉันมีปัญหาที่คล้ายกันซึ่งฉันต้องการ <b> ตั้งค่า </b> ชื่อการทดสอบเนื่องจากฉันใช้นักวิ่ง Parametrized ที่ให้เฉพาะกรณีทดสอบที่มีหมายเลขเท่านั้น
Volker Stolz

วิธีการแก้ปัญหาที่น่ารักเมื่อใช้ Test หรือ TestWatcher ... แค่สงสัย (เสียงดัง) ว่าควรจะมีสิ่งนี้หรือไม่? คุณสามารถค้นหาว่าการทดสอบทำงานช้าหรือไม่โดยดูที่ตารางแสดงผลการกำหนดเวลาที่กำหนดโดย Gradle คุณไม่จำเป็นต้องรู้คำสั่งที่การทดสอบทำงาน ...
mike rodent

คำตอบ:


378

JUnit 4.7 เพิ่มคุณสมบัตินี้ดูเหมือนว่าการใช้ภาษาอังกฤษ-กฎ ดูเหมือนว่าคุณจะได้รับชื่อวิธี:

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

4
นอกจากนี้โปรดทราบว่า TestName ไม่สามารถใช้ได้ใน @before :( ดู: old.nabble.com/ ......
jm.

41
เห็นได้ชัดว่าเวอร์ชันที่ใหม่กว่าของ JUnit @Ruleจะเรียกใช้งานมาก่อน@Before- ฉันใหม่กับ JUnit และขึ้นอยู่กับTestNameฉัน@Beforeโดยไม่มีปัญหาใด ๆ
MightyE

9
มีวิธีที่มีประสิทธิภาพมากขึ้นในการทำที่มีอยู่นี้
ดันแคนโจนส์

2
หากคุณใช้การทดสอบแบบพารามิเตอร์ "name.getMethodName ()" จะส่งกลับ {testA [0], testA [1], ฯลฯ } ดังนั้นฉันจึงใช้งานบางอย่างเช่น: assertTrue (name.getMethodName (). match ("testA (\ [\ \ d \]) "));
Legna

2
@DuncanJones ทำไมตัวเลือกที่เสนอจึงมีประสิทธิภาพมากกว่า
เตฟาน

117

JUnit 4.9.x และสูงกว่า

ตั้งแต่ JUnit 4.9 TestWatchmanคลาสเลิกใช้แล้วซึ่งเป็นที่โปรดปรานของTestWatcherคลาสซึ่งมีการเรียกใช้:

@Rule
public TestRule watcher = new TestWatcher() {
   protected void starting(Description description) {
      System.out.println("Starting test: " + description.getMethodName());
   }
};

หมายเหตุ: จะต้องประกาศคลาสที่มีpublicอยู่

JUnit 4.7.x - 4.8.x

วิธีการต่อไปนี้จะพิมพ์ชื่อวิธีการสำหรับการทดสอบทั้งหมดในชั้นเรียน:

@Rule
public MethodRule watchman = new TestWatchman() {
   public void starting(FrameworkMethod method) {
      System.out.println("Starting test: " + method.getName());
   }
};

1
@ takacsot นั่นเป็นเรื่องที่น่าแปลกใจ คุณช่วยโพสต์คำถามใหม่เกี่ยวกับเรื่องนี้และส่งลิงก์มาให้ฉันได้ไหม
Duncan Jones

เหตุใดจึงต้องใช้publicฟิลด์
Raffi Khatchadourian


16

JUnit 5 และสูงกว่า

ในJUnit 5คุณสามารถฉีดTestInfoซึ่งทำให้การทดสอบ meta data นั้นง่ายขึ้นสำหรับวิธีทดสอบ ตัวอย่างเช่น:

@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


9

ลองใช้สิ่งนี้แทน:

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

หมายเหตุ: สิ่งนี้ใช้ไม่ได้หากการทดสอบของคุณเป็นคลาสย่อยของTestCase ! การทดสอบทำงาน แต่รหัส@Ruleไม่เคยทำงาน


3
พระเจ้าอวยพรคุณสำหรับบันทึกย่อของคุณในตัวอย่าง
user655419

"นี่ใช้งานไม่ได้" - กรณีตรงจุด - แตงกวาไม่สนใจคำอธิบายประกอบ
@Rule

8

พิจารณาใช้ SLF4J (Simple Logging Facade สำหรับ 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() {

  }
}

6

วิธีที่ซับซ้อนคือการสร้างนักวิ่งของคุณเองโดย subclassing 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 superclass หากคุณไม่ต้องการจดจำมันทุกครั้ง แน่นอนว่านี่ จำกัด การเลือกนักวิ่งของคุณ แต่อาจเป็นที่ยอมรับได้

นอกจากนี้อาจใช้กังฟูเล็กน้อยเพื่อรับชื่อการทดสอบปัจจุบันออกจาก Runner และเข้าสู่กรอบงานของคุณ แต่อย่างน้อยก็ทำให้คุณได้รับชื่อ


อย่างน้อยในแนวความคิดความคิดนี้ดูเหมือนจะตรงไปตรงมากับฉัน ประเด็นของฉัน: ฉันจะไม่เรียกมันว่าซับซ้อน
user98761

"on a test superclass ... " - ได้โปรดรูปแบบการออกแบบที่สืบทอดกันมาไม่น่ากลัวอีกต่อไป นี่คือ JUnit3!
oberlies

3

JUnit 4 ไม่มีกลไกนอกกรอบสำหรับกรณีทดสอบเพื่อรับชื่อเป็นของตัวเอง (รวมถึงระหว่างการตั้งค่าและการฉีกขาด)


1
มีกลไกที่ไม่ออกนอกกรอบนอกเหนือไปจากการตรวจสอบสแต็กหรือไม่?
34490 Dave Ray Ray

4
ไม่ใช่กรณีที่ได้รับคำตอบด้านล่าง! อาจจะกำหนดคำตอบที่ถูกต้องให้กับคนอื่น?
Toby

3
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) {
    }
}

1
ผมสามารถยืนยันว่าเขาต้องการที่จะแสดงวิธีการแก้ปัญหา .. ไม่เห็นว่าทำไมการโหวตลบ .... @downvoter: อย่างน้อยอย่างน้อยเพิ่มข้อมูลที่เป็นประโยชน์ ..
วิคเตอร์

1
@skaffman เราทุกคนชอบที่จะเห็นทางเลือกแบบเต็มรูปแบบ นี่คือสิ่งที่ใกล้เคียงที่สุดกับสิ่งที่ฉันกำลังมองหา: การได้รับชื่อการทดสอบไม่ได้อยู่ใน testclass โดยตรง แต่ในชั้นเรียนที่จะถูกใช้ในระหว่างการทดสอบ (ตัวอย่างเช่นที่ใดที่หนึ่งในองค์ประกอบตัวบันทึก) คำอธิบายประกอบที่เกี่ยวข้องกับการทดสอบจะไม่ทำงานอีกต่อไป
Daniel Alder

3

จากความคิดเห็นก่อนหน้าและพิจารณาเพิ่มเติมฉันสร้างส่วนขยายของ TestWather ซึ่งคุณสามารถใช้ในวิธีการทดสอบ JUnit ของคุณด้วยสิ่งนี้:

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

สนุก!


สวัสดีคืออะไรImportUtilsTestฉันได้รับข้อผิดพลาดดูเหมือนว่าจะเป็นคลาสตัวบันทึกฉันมีข้อมูลเพิ่มเติมหรือไม่ ขอบคุณ
Sylhare

1
คลาสที่มีชื่อเป็นเพียงตัวอย่างของคลาสทดสอบ JUnit: ผู้ใช้ของ JUnitHelper ฉันจะแก้ไขตัวอย่างการใช้งาน
Csaba Tenkes

อาตอนนี้ฉันรู้สึกโง่มันชัดเจนมาก ขอบคุณมาก! ;)
Sylhare

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

0

ฉันขอแนะนำให้คุณแยกชื่อวิธีทดสอบจากชุดข้อมูลการทดสอบของคุณ ฉันจะสร้างแบบจำลองคลาส DataLoaderFactory ซึ่งโหลด / แคชชุดข้อมูลทดสอบจากแหล่งข้อมูลของคุณจากนั้นในกรณีทดสอบของคุณเรียกใช้วิธีการอินเทอร์เฟซที่ส่งคืนชุดข้อมูลทดสอบสำหรับกรณีทดสอบ การมีข้อมูลการทดสอบเชื่อมโยงกับชื่อวิธีทดสอบถือว่าข้อมูลการทดสอบสามารถใช้ได้เพียงครั้งเดียวซึ่งในกรณีส่วนใหญ่ฉันขอแนะนำว่าข้อมูลการทดสอบเดียวกันที่ใช้ในการทดสอบหลายครั้งเพื่อตรวจสอบแง่มุมต่างๆของตรรกะทางธุรกิจของคุณ


0

คุณสามารถทำได้โดยใช้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());
    }
};

0

ใน JUnit 5 TestInfoทำหน้าที่แทนการดรอปดาวน์สำหรับกฎ TestName จาก JUnit 4

จากเอกสาร:

TestInfo ใช้เพื่อฉีดข้อมูลเกี่ยวกับการทดสอบปัจจุบันหรือคอนเทนเนอร์ลงใน @Test, @RepeatTest, @ParameterizedTest, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll และ @AfterAll

เพื่อดึงชื่อวิธีของการทดสอบดำเนินการในปัจจุบันคุณมีสองตัวเลือก: และ String TestInfo.getDisplayName() Method TestInfo.getTestMethod()

ที่จะดึงเฉพาะชื่อของวิธีการทดสอบในปัจจุบัน อาจจะไม่เพียงพอเป็นวิธีการทดสอบชื่อที่แสดงเริ่มต้นคือTestInfo.getDisplayName() การทำซ้ำชื่อเมธอดไม่จำเป็นต้องเป็นความคิดที่ดี methodName(TypeArg1, TypeArg2, ... TypeArg3)
@DisplayName("..")

คุณสามารถใช้ TestInfo.getTestMethod()สิ่งนั้นคืนค่าOptional<Method>วัตถุ
หากมีการใช้วิธีเรียกคืนภายในวิธีทดสอบคุณไม่จำเป็นต้องทดสอบ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());
}

0

JUnit 5 ผ่านทาง ExtensionContext

ความได้เปรียบ:

คุณจะได้รับจะมีฟังก์ชันการทำงานที่เพิ่มขึ้นของโดยเอาชนะExtensionContextafterEach(ExtensionContext context)

public abstract class BaseTest {

    protected WebDriver driver;

    @RegisterExtension
    AfterEachExtension afterEachExtension = new AfterEachExtension();

    @BeforeEach
    public void beforeEach() {
        // Initialise driver
    }

    @AfterEach
    public void afterEach() {
        afterEachExtension.setDriver(driver);
    }

}
public class AfterEachExtension implements AfterEachCallback {

    private WebDriver driver;

    public void setDriver(WebDriver driver) {
        this.driver = driver;
    }

    @Override
    public void afterEach(ExtensionContext context) {
        String testMethodName = context.getTestMethod().orElseThrow().getName();
        // Attach test steps, attach scsreenshots on failure only, etc.
        driver.quit();
    }

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.