簡體   English   中英

當我有許多 SpringBootTest 類時,如何有效地使用嵌套配置類來注入依賴項

[英]How can I efficiently use nested configuration classes to inject dependencies when I have many SpringBootTest classes

這個問題是Can I use code to control the dependency resolution Decisions made by ApplicationContext in Spring Boot?

公認的答案是在每個@SpringBootTest測試夾具 class 中定義一個嵌套的 class,用@TestConfiguration對其進行注釋,並在其中為每個需要解析的 bean 定義一個工廠方法。 嵌套類的影響范圍僅限於測試夾具,影響夾具中的所有測試,但不影響其他夾具中定義的測試。

當在每個測試夾具中運行測試時,這提供了對注入到組件中的依賴項的細粒度控制。

這種方法的問題在於,它需要在每個測試夾具 class 中添加嵌套解析器 class。 這是不可擴展的。 考慮一個有 10 個測試裝置的項目。 其中 9 個使用相同的注入依賴項,只有第 10 個需要針對一個特定接口的不同實現。

在這種情況下,我需要將測試配置 class 復制到 9 個測試夾具類中,並僅在第 10 次測試中使用第二個配置 class。

我需要一種更具可擴展性的方式來做到這一點。 例如,在上面的例子中,我希望能夠定義兩個配置類,一個用於測試夾具使用的兩個配置中的每一個。 然后我希望能夠為每個測試夾具指定應該使用兩個配置類中的哪一個。 我努力了:

  1. 我嘗試使用@Import注釋將一個文本夾具的嵌套配置 class 導入另一個測試夾具,但是這樣做時,配置 class 在后者中被忽略。
  2. 我還嘗試將嵌套配置 class 移動到上層,以便它可以用於所有未明確定義不同的測試夾具作為嵌套 class,但在這種情況下,配置 ZA2F2ED4F8EBC2CBB4C21A29DC40AB1 測試夾具全部忽略。

因此,總而言之,我正在尋找一種有效的方法,它允許我只編寫每個配置 class 一次,然后選擇性地將一個配置應用於每個 SpringBootTest class 而無需復制它。

經過一些實驗,我達到了以下解決方案。 我將添加所有細節來總結我在上一個問題中學到的東西。

背景

  1. 我們有兩個接口:IClient 和 IServer
  2. IClient 有兩種實現方式:RealClient 和 MockClient。
  3. IServer 有兩種實現方式:RealServer 和 MockServer。

要求

  1. 生產代碼(在 main/java 中)應該使用兩者的 Real 實現。
  2. 測試夾具(在 test/java 中用 @SpringBootTest 注釋)

    • InterfaceTests 定義了應該使用 MockServer 和 MockClient 的測試
    • ClientTests 定義了應該使用 MockServer 和 RealClient 來測試 RealClient 的測試。
    • ServerTests 定義了應該使用 MockClient 和 RealServer 來測試 RealServer 的測試。
    • IntegrationTests 定義了應該使用 RealServer 和 RealClient 的測試

從上面可以清楚地看出,模擬/真實客戶端/服務器有四種組合,並且在代碼的某些區域需要每種組合。

解決方案

此解決方案利用 @Configuration 和 @TestConfiguration 注釋來實現這些要求,而無需重復代碼。

  1. 不要使用 @Component 注釋接口及其實現
  2. 在 main/java 下實現配置 class 如下:

@Configuration
public class RealInjector {
    @Bean
    public IServer createServer(){
        return new RealServer();
    }

    @Bean
    public IClient createClient(){
        return new RealClient();
    }
}

  1. 在test/java下實現這三個測試配置類
@TestConfiguration
public class AllMockInjector {
    @Bean
    public IServer createServer(){
        return new MockServer();
    }

    @Bean
    public IClient createClient(){
        return new MockClient();
    }
}

@TestConfiguration
public class MockServerInjector{
    @Bean
    public IServer createServer(){
        return new MockServer();
    }

    @Bean
    public IClient createClient(){
        return new RealClient();
    }
}

@TestConfiguration
public class MockClientInjector{
    @Bean
    public IServer createServer(){
        return new RealServer();
    }

    @Bean
    public IClient createClient(){
        return new MockClient();
    }
}

  1. 如下注釋 InterfaceTests 測試夾具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {AllMockInjector.class})
public class InterfaceTests { ... }
  1. 如下注釋 ClientTests 測試夾具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockServerInjector.class})
public class ClientTests { ... }
  1. 如下注釋 ServerTests 測試夾具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {MockClientInjector.class})
public class ServerTests { ... }
  1. 如下注釋 IntegrationTests 測試夾具:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {RealInjector.class})
public class IntegrationTests { ... }

最后

為了讓測試配置類覆蓋來自 main/java 的 RealInjector 配置 class,我們需要設置屬性:

spring.main.allow-bean-definition-overriding=true 

一種方法是對上述每個測試夾具進行如下注釋:

@SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
class TestFixture { ... }

但這非常冗長,特別是如果您有很多測試裝置。 相反,您可以在 test/resources 下的 application.properties 文件中添加以下內容:

spring.main.allow-bean-definition-overriding=true

您可能還需要在 main/resources 下的 application.properties 中添加它。

概括

此解決方案使您可以對注入到您的代碼中以用於生產和測試的實現進行細粒度控制。 該解決方案不需要代碼重復或外部配置文件(除了 test/resources/application.properties 中的一個屬性)。

暫無
暫無

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

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