Sử dụng lại ngữ cảnh ứng dụng mùa xuân trên các lớp thử nghiệm tháng sáu


83

Chúng tôi có một loạt các trường hợp kiểm thử JUnit (Kiểm thử tích hợp) và chúng được nhóm một cách hợp lý thành các lớp kiểm tra khác nhau.

Chúng tôi có thể tải ngữ cảnh ứng dụng Spring một lần cho mỗi lớp thử nghiệm và sử dụng lại nó cho tất cả các trường hợp thử nghiệm trong lớp thử nghiệm JUnit như được đề cập trong http://static.springsource.org/spring/docs/current/spring-framework-reference /html/testing.html

Tuy nhiên, chúng tôi chỉ tự hỏi liệu có cách nào để tải ngữ cảnh ứng dụng Spring chỉ một lần cho một loạt các lớp thử nghiệm JUnit hay không.

FWIW, chúng tôi sử dụng Spring 3.0.5, JUnit 4.5 và sử dụng Maven để xây dựng dự án.


5
Tất cả các câu trả lời bên dưới đều tuyệt vời, nhưng tôi không có context.xml. Tôi đã chú thích con đường của mình vào quên lãng chưa? Có cách nào để làm điều này mà không có context.xml không?
markthegrea

2
bạn đã tìm thấy câu trả lời cho giải pháp của bạn chưa? tôi gặp vấn đề tương tự và tôi muốn hoàn thành việc này bằng chú thích và Spring Boot.
AleksandarT

Câu trả lời:


96

Vâng, điều này là hoàn toàn có thể. Tất cả những gì bạn phải làm là sử dụng cùng một locationsthuộc tính trong các lớp thử nghiệm của mình:

@ContextConfiguration(locations = "classpath:test-context.xml")

Spring lưu trữ các ngữ cảnh ứng dụng theo locationsthuộc tính nên nếu cùng một ngữ cảnh locationsxuất hiện lần thứ hai, Spring sẽ sử dụng cùng một ngữ cảnh thay vì tạo một ngữ cảnh mới.

Tôi đã viết một bài báo về tính năng này: Tăng tốc kiểm tra tích hợp Spring . Ngoài ra, nó được mô tả chi tiết trong tài liệu Spring: 9.3.2.1 Quản lý ngữ cảnh và bộ nhớ đệm .

Điều này có một hàm ý thú vị. Bởi vì Spring không biết khi nào JUnit được hoàn thành, nó sẽ lưu trữ tất cả các ngữ cảnh mãi mãi và đóng chúng bằng cách sử dụng JVM shutdown hook. Hành vi này (đặc biệt là khi bạn có nhiều lớp thử nghiệm với các lớp khác nhau locations) có thể dẫn đến việc sử dụng quá nhiều bộ nhớ, rò rỉ bộ nhớ, v.v. Một ưu điểm khác của ngữ cảnh bộ nhớ đệm.


Ah! Không nhận ra điều đó. Chúng tôi đã theo cách tiếp cận này trong một thời gian dài và tôi (nhầm lẫn) đã quy khoảng thời gian dài cho việc thực thi thử nghiệm là tải ngữ cảnh mùa xuân với mọi lớp thử nghiệm. Sẽ kiểm tra cẩn thận ngay bây giờ. Cảm ơn.
Ramesh

1
Tôi thà nói rằng, mùa xuân đó không biết gì về thứ tự thực thi của các tủ thử nghiệm của bạn. Do đó, nó không thể biết liệu ngữ cảnh có được yêu cầu sau này hay không, hay có thể được xử lý.
philnate

1
Tôi không hiểu làm thế nào điều này thực sự có thể đúng. Eclipse / JUnit dành 2 phút để tạo ra môi trường mỗi khi tôi thực hiện kiểm tra Run As / JUnit. Điều này sẽ không xảy ra nếu bất cứ điều gì được lưu vào bộ nhớ cache.
user1944491

3
Bất kỳ ý tưởng nào nếu điều này có thể được thực hiện đầy đủ thông qua các chú thích thay vì sử dụng XML để định nghĩa ngữ cảnh? Tôi đã tìm kiếm rất nhiều về nó trong tài liệu và ở đây trên SO nhưng không thể tìm thấy bất cứ điều gì khiến tôi nghĩ rằng điều đó là không thể.
Jean-François Savard

điều này không đúng nếu bạn có bộ khởi tạo? của tôi đang khởi tạo cho mọi bài kiểm tra trong lớp
Kalpesh Soni

26

Để thêm vào câu trả lời của Tomasz Nurkiewicz , kể từ Spring 3.2.2, @ContextHierarchychú thích có thể được sử dụng để có cấu trúc ngữ cảnh nhiều liên kết riêng biệt. Điều này rất hữu ích khi nhiều lớp thử nghiệm muốn chia sẻ (ví dụ) thiết lập cơ sở dữ liệu trong bộ nhớ (nguồn dữ liệu, EntityManagerFactory, trình quản lý tx, v.v.).

Ví dụ:

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("FirstTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class FirstTest {
 ...
}

@ContextHierarchy({
  @ContextConfiguration("/test-db-setup-context.xml"),
  @ContextConfiguration("SecondTest-context.xml")
})
@RunWith(SpringJUnit4ClassRunner.class)
public class SecondTest {
 ...
}

Bằng cách thiết lập này, ngữ cảnh sử dụng "test-db-setup-context.xml" sẽ chỉ được tạo một lần, nhưng các bean bên trong nó có thể được đưa vào ngữ cảnh của thử nghiệm đơn vị riêng lẻ

Tìm hiểu thêm về hướng dẫn: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (tìm kiếm " phân cấp ngữ cảnh ")


Tôi đã maven nhiều mô-đun và tôi cố gắng tránh thiết lập cơ sở dữ liệu trong mô-đun dịch vụ (vì nó đã được tải với các thử nghiệm của mô-đun truy cập dữ liệu) và nó không hoạt động với tôi!
Muhammad Hewedy

5
Điều này đã làm việc cho tôi! Cảm ơn. Nói rõ hơn, không có chú thích @ContextHierarchy, mùa xuân sẽ tải db của tôi cho mỗi bài kiểm tra. Tôi đang sử dụng tham số "lớp": @ContextConfiguration (các lớp = {JpaConfigTest.class, ...
Brel

5
Bất kỳ ý tưởng nào nếu điều này có thể được thực hiện đầy đủ thông qua các chú thích thay vì sử dụng XML để định nghĩa ngữ cảnh? Tôi đã tìm kiếm rất nhiều về nó trong tài liệu và ở đây trên SO nhưng không thể tìm thấy bất cứ điều gì khiến tôi nghĩ rằng điều đó là không thể.
Jean-François Savard

1
@ Jean-FrançoisSavard bạn có gặp may mắn trong các tìm kiếm của mình không (thông qua annotationsw thay vì XML)?
javadev

@javadev Tôi hy vọng điều này là những gì bạn đang tìm kiếm docs.spring.io/spring/docs/current/spring-framework-reference/...
Raviteja Gubba

1

Về cơ bản, spring đủ thông minh để định cấu hình điều này cho bạn nếu bạn có cùng cấu hình ngữ cảnh ứng dụng trên các lớp thử nghiệm khác nhau. Ví dụ, giả sử bạn có hai lớp A và B như sau:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

Trong ví dụ này, lớp A chế nhạo bean C, trong khi lớp B chế nhạo bean D. Vì vậy, spring coi đây là hai cấu hình khác nhau và do đó sẽ tải ngữ cảnh ứng dụng một lần cho lớp A và một lần cho lớp B.

Nếu thay vào đó, chúng tôi muốn có Spring chia sẻ bối cảnh ứng dụng giữa hai lớp này, chúng sẽ phải trông giống như sau:

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class A {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

@ActiveProfiles("h2")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class B {

    @MockBean
    private C c;

    @MockBean
    private D d;
    //Autowired fields, test cases etc...
}

Nếu bạn sắp xếp các lớp của mình như thế này, spring sẽ tải ngữ cảnh ứng dụng chỉ một lần cho lớp A hoặc B tùy thuộc vào lớp nào trong số hai lớp được chạy trước trong bộ thử nghiệm. Điều này có thể được nhân rộng trên nhiều lớp thử nghiệm, chỉ có tiêu chí là bạn không nên tùy chỉnh các lớp thử nghiệm một cách khác nhau. Bất kỳ tùy chỉnh nào dẫn đến lớp thử nghiệm khác với lớp khác (trong mắt của mùa xuân) cuối cùng sẽ tạo ra một bối cảnh ứng dụng khác vào mùa xuân.


0

tạo lớp cấu hình của bạn như bên dưới

@ActiveProfiles("local")
@RunWith(SpringJUnit4ClassRunner.class )
@SpringBootTest(classes ={add your spring beans configuration classess})
@TestPropertySource(properties = {"spring.config.location=classpath:application"})
@ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
public class RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class);


    //auto wire all the beans you wanted to use in your test classes
    @Autowired
    public XYZ xyz;
    @Autowired
    public ABC abc;


    }



Create your test suite like below



@RunWith(Suite.class)
@Suite.SuiteClasses({Test1.class,test2.class})
public class TestSuite extends RunConfigration {

    private ClassLoader classloader = Thread.currentThread().getContextClassLoader();

    private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class);


}

Tạo các lớp thử nghiệm của bạn như bên dưới

public class Test1 extends RunConfigration {


  @Test
    public void test1()
    {
    you can use autowired beans of RunConfigration classes here 
    }

}


public class Test2a extends RunConfigration {

     @Test
    public void test2()
    {
    you can use autowired beans of RunConfigration classes here 
    }


}

0

Một điểm đáng chú ý là nếu chúng ta sử dụng @SpringBootTests nhưng một lần nữa use @MockBean in different test classes, Spring không có cách nào để sử dụng lại ngữ cảnh ứng dụng của nó cho tất cả các bài kiểm tra.

Giải pháp là to move all @MockBean into an common abstract classvà khắc phục sự cố.

@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class)
public abstract class AbstractIT {

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

Sau đó, các lớp thử nghiệm có thể được nhìn thấy như dưới đây

public class ProductControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchProduct_ShouldSuccess() {
   }

}

public class InvoiceControllerIT extends AbstractIT {
   // please don't use @MockBean here
   @Test
   public void searchInvoice_ShouldSuccess() {
   }

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