簡體   English   中英

跨junit測試類重用spring應用程序上下文

[英]Reuse spring application context across junit test classes

我們有一堆 JUnit 測試用例(集成測試),它們在邏輯上被分組到不同的測試類中。

我們能夠為每個測試類加載一次 Spring 應用程序上下文,並將其重新用於 JUnit 測試類中的所有測試用例,如http://static.springsource.org/spring/docs/current/spring-framework-reference 中所述/html/testing.html

然而,我們只是想知道是否有一種方法可以為一堆 JUnit 測試類只加載一次 Spring 應用程序上下文。

FWIW,我們使用 Spring 3.0.5、JUnit 4.5 並使用 Maven 構建項目。

是的,這是完全可能的。 你所要做的就是在你的測試類中使用相同的locations屬性:

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

Spring 通過locations屬性緩存應用程序上下文,因此如果第二次出現相同的locations ,Spring 將使用相同的上下文而不是創建新的上下文。

我寫了一篇關於這個特性的文章: 加速 Spring 集成測試 在 Spring 文檔中也有詳細描述: 9.3.2.1 Context management and caching

這有一個有趣的含義。 因為 Spring 不知道 JUnit 何時完成,它永遠緩存所有上下文並使用 JVM 關閉鈎子關閉它們。 這種行為(特別是當你有很多不同locations的測試類時)可能會導致內存使用過多、內存泄漏等。緩存上下文的另一個優點。

要添加到Tomasz Nurkiewicz 的答案,從 Spring 3.2.2 開始, @ContextHierarchy注釋可用於具有單獨的、關聯的多上下文結構。 當多個測試類想要共享(例如)內存數據庫設置(數據源、EntityManagerFactory、tx 管理器等)時,這很有用。

例如:

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

通過進行此設置,使用“test-db-setup-context.xml”的上下文將只創建一次,但其中的 bean 可以注入到單個單元測試的上下文中

有關手冊的更多信息: http : //docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (搜索“ 上下文層次結構”)

如果您在不同的測試類中具有相同的應用程序上下文配置,那么基本上 spring 足夠聰明,可以為您配置它。 例如,假設您有兩個類 A 和 B,如下所示:

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

在此示例中,類 A 模擬 bean C,而類 B 模擬 bean D。因此,spring 將它們視為兩種不同的配置,因此將為類 A 加載應用程序上下文一次,為類 B 加載一次。

相反,如果我們希望 spring 在這兩個類之間共享應用程序上下文,則它們必須如下所示:

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

如果你像這樣連接你的類,spring 將只為類 A 或 B 加載應用程序上下文一次,具體取決於這兩個類中的哪個類在測試套件中首先運行。 這可以跨多個測試類復制,唯一的標准是您不應以不同的方式自定義測試類。 任何導致測試類與其他類不同的定制(在 spring 眼中)最終都會由 spring 創建另一個應用程序上下文。

一個值得注意的點是,如果我們使用 @SpringBootTests 但use @MockBean in different test classes再次use @MockBean in different test classes ,則 Spring 無法為所有測試重用其應用程序上下文。

解決方案是to move all @MockBean into an common abstract class並解決問題。

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

   @MockBean
   private ProductService productService;

   @MockBean
   private InvoiceService invoiceService;

}

然后可以看到測試類如下

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

}

創建您的配置類,如下所示

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


}

創建您的測試類,如下所示

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 
    }


}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM