Một giải pháp cho vấn đề đầu tiên là để di chuyển logic thành một phần mở rộng của org.junit.rules.ExternalResource
nối với các thử nghiệm thông qua một @ClassRule
, được giới thiệu trong JUnit 4.9:
public class MyTest {
@ClassRule
public static final TestResources res = new TestResources();
@Test
public void testFoo() {
// test logic here
}
}
public class TestResources extends ExternalResource {
protected void before() {
// Setup logic that used to be in @BeforeClass
}
protected void after() {
// Setup logic that used to be in @AfterClass
}
}
Bằng cách này, các nguồn lực trước đó được quản lý bởi lớp cơ sở được di chuyển ra khỏi hệ thống phân cấp lớp thử nghiệm và thành nhiều tài nguyên mô-đun/tiêu hao hơn có thể được tạo trước khi một lớp chạy và bị phá hủy sau khi một lớp chạy.
Để giải quyết cả hai vấn đề cùng một lúc - tức là: có cùng một thiết lập/teardown ở mức cao như là một phần của một thử nghiệm riêng lẻ và như một phần của bộ - dường như không có bất kỳ công cụ cụ thể nào hỗ trợ cho việc này. Tuy nhiên ..., bạn có thể thực hiện nó cho mình:
Đơn giản chỉ cần thay đổi tạo @ClassRule
nguồn thành một mô hình nhà máy mà không tham khảo đếm trong nội bộ để xác định có hay không để tạo/phá hủy các tài nguyên.
Ví dụ (xin lưu ý đây là khó khăn và có thể cần một số chỉnh/error handling vững mạnh):
public class TestResources extends ExternalResource {
private static int refCount = 0;
private static TestResources currentInstance;
public static TestResources getTestResources() {
if (refCount == 0) {
// currentInstance either hasn't been created yet, or after was called on it - create a new one
currentInstance = new TestResources();
}
return currentInstance;
}
private TestResources() {
System.out.println("TestResources construction");
// setup any instance vars
}
protected void before() {
System.out.println("TestResources before");
try {
if (refCount == 0) {
System.out.println("Do actual TestResources init");
}
}
finally {
refCount++;
}
}
protected void after() {
System.out.println("TestResources after");
refCount--;
if (refCount == 0) {
System.out.println("Do actual TestResources destroy");
}
}
}
Cả hai bạn bộ/lớp học thử nghiệm sẽ chỉ sử dụng các tài nguyên như một @ClassResource
thông qua phương pháp nhà máy:
@RunWith(Suite.class)
@SuiteClasses({FooTest.class, BarTest.class})
public class MySuite {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@BeforeClass
public static void suiteSetup() {
System.out.println("Suite setup");
}
@AfterClass
public static void suiteTeardown() {
System.out.println("Suite teardown");
}
}
public class FooTest {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@Test
public void testFoo() {
System.out.println("testFoo");
}
}
public class BarTest {
@ClassRule
public static TestResources res = TestResources.getTestResources();
@Test
public void testBar() {
System.out.println("testBar");
}
}
Khi chạy thử nghiệm riêng lẻ, việc đếm ngược sẽ không có hiệu lực - "thực tế init" và "teardown thực tế" sẽ chỉ xảy ra một lần. Khi chạy qua bộ phần mềm, bộ sẽ tạo TestResource, và các bài kiểm tra cá nhân sẽ chỉ sử dụng lại phần đã được tạo sẵn (việc đếm ngược giữ cho nó không bị phá hủy và tái tạo lại giữa các bài kiểm tra trong bộ phần mềm).
Tôi nghĩ rằng hệ thống refCount của bạn không hoạt động. Nó sẽ nhận được không sau mỗi lớp và xóa tài nguyên của bạn và sau đó reinitialize nó vào lớp tiếp theo khi gọi getTestResources(). Tôi có thể thiếu một cái gì đó, sửa tôi nếu tôi sai. – FredBoutin
Ý tưởng là nó có thể khởi tạo khi chạy một thử nghiệm riêng lẻ hoặc một bộ kiểm thử. ** Khi chạy như một bộ **, refCount được đặt thành 1 bởi bộ 'ClassRule'. Mỗi bài kiểm tra sẽ thay đổi refCount thành 2, sau đó quay lại 1 khi hoàn thành. Khi bộ phần mềm được thực hiện, nó sẽ chuyển thành 0. ** Khi chạy thử nghiệm **, refCount được đặt thành 1 bằng phép thử riêng lẻ, sau đó là 0 khi thử nghiệm hoàn tất. Vấn đề là tài nguyên chỉ được tạo và hủy một lần trong cả hai cách chạy. – Krease
Cảm ơn rõ ràng hơn nhiều hơn @Krease! – FredBoutin