簡體   English   中英

如何在一次測試中測試多個 Spring Boot 應用程序?

[英]How to test multiple Spring Boot applications in one test?

我有一個帶有 2 個 Spring 引導應用程序的多模塊 Maven 項目

父母

  • fooApp
  • 酒吧應用
  • 測試

如何設置測試,您可以在同一過程中加載單獨的 spring 引導應用程序,每個應用程序都有自己的配置上下文。

public abstract class AbstractIntegrationTest {//test module

    protected FOO foo;
    protected BAR bar;

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @IntegrationTest
    @Transactional
    @SpringApplicationConfiguration(classes = foo.Application.class)
    public class FOO {
        public MockMvc mockMvc;

        @Autowired
        public WebApplicationContext wac;

        @Before
        public void _0_setup() {
            MockitoAnnotations.initMocks(this);
            mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
            TestCase.assertNotNull(mockMvc);
        }

        public void login(String username) {
        }
    }

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @IntegrationTest
    @Transactional
    @SpringApplicationConfiguration(classes = bar.Application.class)
    public class BAR {

        @Autowired
        public WebApplicationContext wac;

        public MockMvc restMvc;

        @Before
        public void _0_setup() {
            MockitoAnnotations.initMocks(this);
            restMvc = MockMvcBuilders.webAppContextSetup(wac).build();
            TestCase.assertNotNull(restMvc);
        }

        public void login(String username) {
        }
    }

    @Before
    public void _0_setup() {
        foo = new FOO();
        bar = new BAR();
    }
}

和一個集成測試的例子

public class IntegrationTest extends AbstractIntegrationTest {

    @Test
    public void login() {
        foo.login("foologin");
        bar.login("barlogin");
    }

}

我同意@rainerhahnekamp 的觀點,他說您想要實現的更像是系統/集成測試。

但是,如果您仍然想以這種方式進行測試,我認為它仍然是可行的。

首先,要知道一件重要的事情
test項目中同時導入fooAppbarApp項目將使兩個項目的配置文件都可用於類加載器,並且會產生不可預測的結果。 示例:將僅加載兩個application.properties文件之一。 因此,您必須使用 2 個不同的配置文件來加載 2 個單獨的配置文件設置。
由於項目文件重疊的相同原因,一個應用程序在另一個應用程序可見的包中定義的 bean 將在兩個應用程序上下文中加載。

為了測試這個概念,我在每個項目中創建了一個服務和一個休息控制器,每個都有一個“配置文件”屬性文件:

酒吧應用


@EnableAutoConfiguration(
    exclude = {SecurityAutoConfiguration.class,
    ManagementWebSecurityAutoConfiguration.class})
@SpringBootApplication
public class BarApp {


  public static void main(String[] args) {
    SpringApplication.run(BarApp.class, args);
  }

}

@Service
public class BarService {

  public String yield() {
    return "BarService !";
  }

}

@RestController
public class BarResource {

  private final BarService barService;

  public BarResource(BarService barService) {
    this.barService = barService;
  }

  @GetMapping("/bar")
  public String getBar() {
    return barService.yield();
  }

}

應用-bar.properties:

server.port=8181

fooApp


@EnableConfigurationProperties
@SpringBootApplication
public class FooApp {
  
  public static void main(String[] args) {
    SpringApplication.run(FooApp.class, args);
  }

}

@Service
public class FooService {

  public String yield() {
    return "FooService !";
  }

}

@RestController
public class FooResource {

  private final FooService fooService;

  public FooResource(FooService fooService) {
    this.fooService = fooService;
  }

  @GetMapping("/foo")
  public String getFoo() {
    return fooService.yield();
  }

}

應用程序-foo.properties:

server.port=8282

測試

class TestApps {

  @Test
  void TestApps() {
    // starting and customizing BarApp
    {
      SpringApplication barApp = new SpringApplication(BarApp.class);
      barApp.setAdditionalProfiles("bar"); // to load 'application-bar.properties'
      GenericWebApplicationContext barAppContext = (GenericWebApplicationContext) barApp.run();

      BarService barServiceMock = Mockito.mock(BarService.class);
      Mockito.doReturn("mockified bar !").when(barServiceMock).yield();
      barAppContext.removeBeanDefinition("barService");
      barAppContext.registerBean("barService", BarService.class, () -> barServiceMock);
    }

    // starting and customizing FooApp
    {
      SpringApplication fooApp = new SpringApplication(FooApp.class);
      fooApp.setAdditionalProfiles("foo"); // to load 'application-foo.properties'
      GenericWebApplicationContext fooAppContext = (GenericWebApplicationContext) fooApp.run();

      FooService fooServiceMock = Mockito.mock(FooService.class);
      Mockito.doReturn("mockified foo !").when(fooServiceMock).yield();
      fooAppContext.removeBeanDefinition("fooService");
      fooAppContext.registerBean("fooService", FooService.class, () -> fooServiceMock);
    }

    RestTemplate restTemplate = new RestTemplate();
    String barResourceUrl = "http://localhost:8181/bar";
    ResponseEntity<String> barResponse = restTemplate.getForEntity(barResourceUrl, String.class);

    String fooResourceUrl = "http://localhost:8282/foo";
    ResponseEntity<String> fooResponse = restTemplate.getForEntity(fooResourceUrl, String.class);

    System.out.println(barResponse.getBody());
    System.out.println(fooResponse.getBody());
  }

}

啟動測試會產生:

mockified bar !
mockified foo !

順便說一句,我懷疑您的項目會像我的示例一樣簡單,並且我懷疑您會遇到與我之前強調的重要事項相關的問題。

您需要配置 @ContextConfiguration 以指向兩個應用程序

給定兩個包 com.foo.module1 和 com.foo.module2,您必須為每個包創建一個配置類。 例如對於模塊 1:

@SpringBootApplication public class Config1 {}

如果您只想使用單個包的 Spring bean 來運行應用程序,您可以使用 SpringApplicationBuilder 來實現。 一個工作片段:

   new SpringApplicationBuilder(com.foo.module1.Config1.class)
     .showBanner(false)
     .run()

這將使用 Config1 啟動 Spring,它僅在其包中搜索(@ComponentScan 包含在 @SpringBootApplication 中)bean。

如果您想運行完整的應用程序,例如同時運行所有兩個模塊,您必須在上層包 com.foo 中創建一個配置類。

在下面提到的情況下,由於 spring-boot-starters 之類的庫,在單個應用程序中運行兩個模塊可能會以不希望的方式相互干擾,我只能想到兩種可能性:

  1. 使用 OSGi:這可能無法完全解決問題,而且可能是一個相當復雜的設置或
  2. 將應用程序拆分為兩個應用程序並創建接口。 Spring Boot 也是微服務架構的不錯選擇。

暫無
暫無

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

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