Nhận tên của bài kiểm tra hiện đang thực hiện trong JUnit 4


240

Trong JUnit 3, tôi có thể nhận được tên của bài kiểm tra hiện đang chạy như thế này:

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

trong đó sẽ in "Thử nghiệm hiện tại là thử nghiệm".

Có cách nào khác hay đơn giản để làm điều này trong JUnit 4 không?

Bối cảnh: Rõ ràng, tôi không muốn chỉ in tên bài kiểm tra. Tôi muốn tải dữ liệu dành riêng cho thử nghiệm được lưu trữ trong tài nguyên có cùng tên với thử nghiệm. Bạn biết đấy, quy ước về cấu hình và tất cả những thứ đó.


Mã trên cung cấp cho bạn những gì trong JUnit 4?
Lập hóa đơn cho thằn lằn

5
Các bài kiểm tra JUnit 3 mở rộng TestCase trong đó getName () được xác định. Các bài kiểm tra JUnit 4 không mở rộng một lớp cơ sở, vì vậy không có phương thức getName () nào cả.
Dave Ray

Tôi có một vấn đề tương tự khi tôi muốn <b> đặt </ b> tên thử nghiệm vì tôi đang sử dụng trình chạy Parametriened chỉ cung cấp cho tôi các trường hợp kiểm tra được đánh số.
Volker Stolz

Giải pháp đáng yêu bằng cách sử dụng Test hoặc TestWatcher ... chỉ cần tự hỏi (lớn tiếng) liệu có cần phải có điều này không? Bạn có thể tìm xem liệu một bài kiểm tra có chạy chậm hay không bằng cách xem các biểu đồ đầu ra thời gian do Gradle đưa ra. Bạn không bao giờ cần phải biết thứ tự kiểm tra hoạt động ...?
mike gặm nhấm

Câu trả lời:


378

JUnit 4.7 đã thêm tính năng này có vẻ như sử dụng TestName-Rule . Có vẻ như điều này sẽ giúp bạn có được tên phương thức:

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
Cũng lưu ý rằng TestName không khả dụng trong @b Before :( Xem: old.nabble.com/ Kẻ
jm.

41
Rõ ràng các phiên bản mới hơn của JUnit đã thực thi @Ruletrước đây @Before- Tôi mới biết về JUnit và tùy thuộc vào TestNametôi @Beforemà không gặp bất kỳ khó khăn nào.
MightyE


2
Nếu bạn đang sử dụng các bài kiểm tra được tham số hóa "name.getMethodName ()" sẽ trả về {testA [0], testA [1], v.v. \ d \])? "));
Legna

2
@DuncanJones Tại sao giải pháp thay thế được đề xuất là "hiệu quả hơn"?
Stephan

117

JUnit 4.9.x trở lên

Kể từ JUnit 4.9, TestWatchmanlớp đã bị từ chối ủng hộ TestWatcherlớp, trong đó có lời gọi:

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

Lưu ý: Lớp chứa phải được khai báo public.

JUnit 4.7.x - 4.8.x

Cách tiếp cận sau đây sẽ in tên phương thức cho tất cả các bài kiểm tra trong một lớp:

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

1
@takacsot Thật đáng ngạc nhiên. Bạn có thể vui lòng gửi một câu hỏi mới về điều này và ping cho tôi liên kết ở đây?
Duncan Jones

Tại sao nên sử dụng một publiclĩnh vực?
Raffi Khatchadourian


16

JUnit 5 trở lên

Trong JUnit 5, bạn có thể tiêm TestInfođể đơn giản hóa dữ liệu meta kiểm tra cung cấp cho các phương thức kiểm tra. Ví dụ:

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

Xem thêm: Hướng dẫn sử dụng JUnit 5 , TestInfo javadoc .


9

Hãy thử điều này thay thế:

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

Đầu ra trông như thế này:

Starting JUnit-test: MyTest testA
Starting JUnit-test: MyTest testB

LƯU Ý: Điều này KHÔNG hoạt động nếu thử nghiệm của bạn là một lớp con của TestCase ! Kiểm tra chạy nhưng mã @Rule không bao giờ chạy.


3
Thiên Chúa chúc lành cho bạn cho LƯU Ý của bạn ở ngay ví dụ.
dùng655419

"Điều này KHÔNG hoạt động" - trường hợp cụ thể - dưa chuột bỏ qua các chú thích
@Rule

8

Việc xem xét sử dụng SLF4J (Mặt tiền ghi nhật ký đơn giản cho Java) cung cấp một số cải tiến gọn gàng bằng cách sử dụng các thông báo được tham số hóa. Kết hợp SLF4J với triển khai quy tắc JUnit 4 có thể cung cấp các kỹ thuật ghi nhật ký lớp kiểm tra hiệu quả hơn.

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

Một cách dễ hiểu là tạo Người chạy của riêng bạn bằng cách phân lớp org.junit.runners.BlockJUnit4ClassRunner.

Sau đó bạn có thể làm một cái gì đó như thế này:

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

Sau đó, đối với mỗi lớp kiểm tra, bạn sẽ cần thêm chú thích @RunWith (NameAwareRunner. Class). Ngoài ra, bạn có thể đặt chú thích đó trên siêu lớp Kiểm tra nếu bạn không muốn nhớ nó mỗi lần. Điều này, tất nhiên, giới hạn lựa chọn người chạy của bạn nhưng điều đó có thể được chấp nhận.

Ngoài ra, có thể cần một chút kung fu để đưa tên bài kiểm tra hiện tại ra khỏi Người chạy và vào khuôn khổ của bạn, nhưng điều này ít nhất giúp bạn có được tên.


Về mặt khái niệm ít nhất, ý tưởng này có vẻ khá đơn giản với tôi. Quan điểm của tôi là: Tôi sẽ không gọi nó là hỗn độn.
user98761

"trên một siêu lớp thử nghiệm ..." - Xin vui lòng, không còn các mẫu thiết kế dựa trên sự kế thừa khủng khiếp. Đây là JUnit3!
oberlies

3

JUnit 4 không có bất kỳ cơ chế vượt trội nào cho trường hợp thử nghiệm để có được tên riêng của nó (bao gồm cả trong quá trình thiết lập và phá bỏ).


1
Có một cơ chế không vượt trội nào ngoài việc kiểm tra ngăn xếp không?
Dave Ray

4
Không phải trường hợp đưa ra câu trả lời dưới đây! có thể gán câu trả lời đúng cho người khác?
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
Tôi có thể lập luận rằng anh ta chỉ muốn đưa ra giải pháp .. không hiểu tại sao phiếu bầu tiêu cực .... @downvoter: ít nhất, ít nhất, thêm thông tin hữu ích ..
Victor

1
@skaffman Tất cả chúng ta đều thích xem đầy đủ các giải pháp thay thế. Đây là cái gần nhất cho những gì tôi đang tìm kiếm: Lấy tên thử nghiệm không trực tiếp trong lớp thử nghiệm mà trong lớp được sử dụng trong quá trình thử nghiệm (ví dụ ở đâu đó trong thành phần logger). Ở đó, các chú thích liên quan đến thử nghiệm không hoạt động nữa.
Daniel Alder

3

Dựa trên nhận xét trước đó và xem xét thêm tôi đã tạo một phần mở rộng của TestWather mà bạn có thể sử dụng trong các phương thức kiểm tra JUnit của mình với điều này:

public class ImportUtilsTest {
    private static final Logger LOGGER = Logger.getLogger(ImportUtilsTest.class);

    @Rule
    public TestWatcher testWatcher = new JUnitHelper(LOGGER);

    @Test
    public test1(){
    ...
    }
}

Lớp người trợ giúp kiểm tra là lớp tiếp theo:

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

Thưởng thức!


Xin chào, đó là gì ImportUtilsTest, tôi gặp lỗi, có vẻ như là một lớp logger, tôi có thêm thông tin không? Cảm ơn
Sylhare

1
Lớp được đặt tên chỉ là một ví dụ về lớp kiểm tra JUnit: người dùng của JUnitHelper. Tôi sẽ sửa ví dụ sử dụng.
Csaba Tenkes

Ah bây giờ tôi cảm thấy ngu ngốc, nó đã quá rõ ràng. Cảm ơn rất nhiều! ;)
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

Tôi khuyên bạn nên tách tên phương thức thử nghiệm khỏi bộ dữ liệu thử nghiệm của mình. Tôi sẽ mô hình hóa một lớp DataLoaderFactory để tải / lưu trữ các bộ dữ liệu thử nghiệm từ tài nguyên của bạn, và sau đó trong trường hợp thử nghiệm của bạn, hãy gọi một số phương thức giao diện trả về một tập hợp dữ liệu thử nghiệm cho trường hợp thử nghiệm. Việc dữ liệu thử nghiệm được gắn với tên phương thức thử nghiệm giả sử dữ liệu thử nghiệm chỉ có thể được sử dụng một lần, trong hầu hết các trường hợp, tôi đề nghị rằng cùng một dữ liệu thử nghiệm được sử dụng trong nhiều thử nghiệm để xác minh các khía cạnh khác nhau của logic kinh doanh của bạn.


0

Bạn có thể đạt được điều này bằng cách sử dụng Slf4jTestWatcher

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

Trong JUnit 5 TestInfohoạt động như một sự thay thế thả xuống cho quy tắc TestName từ JUnit 4.

Từ tài liệu:

TestInfo được sử dụng để đưa thông tin về thử nghiệm hiện tại hoặc vùng chứa vào các phương thức @Test, @RepeatTest, @ParameterizedTest, @TestFactory, @B BeforeEach, @AfterEach, @B BeforeAll và @AfterAll.

Để lấy tên phương thức của kiểm tra thực hiện hiện tại, bạn có hai tùy chọn: String TestInfo.getDisplayName()Method TestInfo.getTestMethod().

Để chỉ truy xuất tên của phương thức thử nghiệm hiện tại TestInfo.getDisplayName()có thể không đủ vì tên hiển thị mặc định của phương thức thử nghiệm là methodName(TypeArg1, TypeArg2, ... TypeArg3).
Sao chép tên phương thức trong @DisplayName("..")không cần thiết là một ý tưởng tốt.

Thay thế, bạn có thể sử dụng TestInfo.getTestMethod()trả về một Optional<Method>đối tượng.
Nếu phương thức truy xuất được sử dụng bên trong phương thức kiểm tra, bạn thậm chí không cần kiểm tra Optionalgiá trị được bọc.

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 thông qua ExtensionContext

Lợi thế:

Bạn có được các chức năng bổ sung ExtensionContextbằng cách ghi đè afterEach(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();
    }

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.