2014-12-09 35 views
13

Hiện tại, tất cả các bài kiểm tra JUnit của tôi mở rộng từ một lớp cơ sở chung cung cấp các phương thức được gắn thẻ với chú thích @BeforeClass@AfterClass. các xét nghiệm để sử dụng.Làm thế nào để chia sẻ JUnit BeforeClass logic giữa các lớp thử nghiệm

này có vẻ vụng về với tôi trong một vài lý do:

  1. Một phần của điểm Junit4 (từ hiểu biết của tôi) là chúng ta không cần thừa kế thử nghiệm cổ điển này nữa.
  2. Khi tôi chạy những thử nghiệm này như một phần của một bộ thay vì cá nhân (mà chúng ta thường làm), các @BeforeClass@AfterClass nhận được viện dẫn nhiều lần, làm chậm lại các bài kiểm tra - chúng tôi thực sự chỉ nên gọi những lần

Điều tôi muốn làm là bằng cách nào đó di chuyển logic BeforeClass/AfterClass hiện tại ra khỏi chuỗi thừa kế và thành một thứ có thể được chia sẻ bởi các bài kiểm tra riêng lẻ và toàn bộ bộ phần mềm.

Việc này có thể thực hiện được không? Nếu vậy, làm thế nào? (Nếu vấn đề, tôi đang sử dụng JUnit 4.7, và nó có thể là một bán khó có thể nâng cấp lên phiên bản khác nhau)

Trả lời

21

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).

+0

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

+1

Ý 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

+0

Cảm ơn rõ ràng hơn nhiều hơn @Krease! – FredBoutin

1

Bạn có thể sử dụng @BeforeClass@AfterClassTRONG SUITE CLASS.

này sẽ chạy các phương pháp trước khi bất kỳ của các lớp học thử nghiệm trong bộ thực hiện và sau khi tất cả các lớp học thử nghiệm kết thúc (tương ứng)

Bằng cách này bạn có thể chạy chúng một lần duy nhất.

//..usual @RunWith etc annotations here 
public class MySuite{ 

@BeforeClass 
public static void setup(){ 

} 

@AfterClass 
public static void tearDown(){ 

} 

} 
+1

Trong khi điều này là tốt đẹp nếu tôi chỉ bao giờ chạy thử nghiệm từ một lớp suite, nó sẽ không hoạt động khi tôi chạy thử nghiệm độc lập (tức là : trong Eclipse, nhấp chuột phải vào bài kiểm tra và chọn "Chạy như kiểm tra JUnit") - bộ biết về các bài kiểm tra, nhưng các bài kiểm tra không biết về bộ ... – Krease

0

Tôi đã gặp vấn đề tương tự (Spring không phải là một lựa chọn và tôi không viết TestSuites trong dự án maven) vì vậy tôi đã viết runner junit đơn giản để giải quyết vấn đề này.

Bạn cần viết lớp SharedResource và đánh dấu thử nghiệm của mình để yêu cầu tài nguyên đó.

public class SampleSharedResource implements SharedResource { 
public void initialize() throws Exception { 
    //init your resource 
} 

}

@RunWith(JUnitSharedResourceRunner.class) 
@JUnitSharedResourceRunner.WithSharedResources({SampleSharedResource.class}) 
public class SharedResourceRunnerATest { 
... 

Nguồn tại https://github.com/eanlr/junit-shared-resources-runner

Các vấn đề liên quan