Java中MongoDB +TestContainer

banq發表於2024-06-10

如何使用 MongoDB 配置 TestContainer,併為資料訪問層和使用 MongoDB 的應用程式編寫整合測試。

TestContainer幫助我們在執行測試之前啟動容器,並透過在程式碼中定義它們來停止它們。

在本教程中,我們將瞭解如何使用 MongoDB配置TestContainers。接下來,我們將瞭解如何為測試建立基礎整合。最後,我們將學習如何使用 TestContainers 進行資料訪問層和應用程式與 MongoDB 的整合測試。

配置
為了在我們的測試中使用帶有 MongoDB 的 TestContainer,我們需要在具有測試範圍的pom.xml檔案中新增以下依賴項:

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>mongodb</artifactId>
    <version>1.18.3</version>
    <scope>test</scope>
</dependency>

我們有三個依賴項。第一個是核心依賴項,它提供 TestContainers 的主要功能,例如啟動和停止容器。下一個依賴項是 TestContainers 的 JUnit 5 擴充套件。最後一個依賴項是 TestContainers 的 MongoDB 模組。

我們需要在我們的機器上安裝Docker來執行 MongoDB 容器。

建立模型
讓我們首先使用@Document註釋建立與Product表對應的實體:

@Document(collection = <font>"Product")
public class Product {
    @Id
    private String id;
    private String name;
    private String description;
    private double price;
   
// standard constructor, getters, setters<i>
}

建立儲存庫
然後,我們將建立 從 MongoRepository擴充套件的ProductRepository 類:

@Repository
public interface ProductRepository extends MongoRepository<Product, String> {
    Optional<Product> findByName(String name);
}

建立 REST 控制器
最後,讓我們透過建立一個控制器來與儲存庫互動,從而公開 REST API :

@RestController
@RequestMapping(<font>"/products")
public class ProductController {
    private final ProductRepository productRepository;
    public ProductController(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    @PostMapping
    public String createProduct(@RequestBody Product product) {
        return productRepository.save(product)
          .getId();
  }
    @GetMapping(
"/{id}")
    public Product getProduct(@PathVariable String id) {
        return productRepository.findById(id)
          .orElseThrow(() -> new RuntimeException(
"Product not found"));
  }
}

TestContainers MongoDB整合基礎
我們將建立一個抽象基類,該基類擴充套件到需要在執行測試之前和之後啟動和停止 MongoDB 容器的所有類:

@Testcontainers
@SpringBootTest(classes = MongoDbTestContainersApplication.class)
public abstract class AbstractBaseIntegrationTest {
    @Container
    static MongoDBContainer mongoDBContainer = new MongoDBContainer(<font>"mongo:7.0").withExposedPorts(27017);
    @DynamicPropertySource
    static void containersProperties(DynamicPropertyRegistry registry) {
        mongoDBContainer.start();
        registry.add(
"spring.data.mongodb.host", mongoDBContainer::getHost);
        registry.add(
"spring.data.mongodb.port", mongoDBContainer::getFirstMappedPort);
    }
}

我們新增了 @Testcontainers 註釋以在我們的測試中啟用TestContainers支援,並新增了@SpringBootTest註釋以啟動Spring Boot應用程式上下文。

我們還定義了一個 MongoDB 容器欄位,該欄位使用mongo:7.0 Docker 映像啟動 MongoDB 容器並公開埠27017。@Container註釋在執行測試之前啟動 MongoDB 容器。

1. 資料訪問層整合測試
資料訪問層整合測試我們的應用程式與資料庫之間的互動。我們將為 MongoDB 資料庫建立一個簡單的資料訪問層併為其編寫整合測試。

讓我們建立擴充套件AbstractBaseIntegrationTest類的資料訪問整合測試類:

public class ProductDataLayerAccessIntegrationTest extends AbstractBaseIntegrationTest {
    @Autowired
    private ProductRepository productRepository;
    <font>// ..<i>
    
}

現在,我們可以為資料訪問層編寫整合測試:

@Test
public void givenProductRepository_whenSaveAndRetrieveProduct_thenOK() {
    Product product = new Product(<font>"Milk", "1L Milk", 10);
    Product createdProduct = productRepository.save(product);
    Optional<Product> optionalProduct = productRepository.findById(createdProduct.getId());
    assertThat(optionalProduct.isPresent()).isTrue();
    Product retrievedProduct = optionalProduct.get();
    assertThat(retrievedProduct.getId()).isEqualTo(product.getId());
}
@Test
public void givenProductRepository_whenFindByName_thenOK() {
    Product product = new Product(
"Apple", "Fruit", 10);
    Product createdProduct = productRepository.save(product);
    Optional<Product> optionalProduct = productRepository.findByName(createdProduct.getName());
    assertThat(optionalProduct.isPresent()).isTrue();
    Product retrievedProduct = optionalProduct.get();
    assertThat(retrievedProduct.getId()).isEqualTo(product.getId());
}

我們建立了兩個場景:第一個場景儲存並檢索產品,第二個場景按名稱查詢產品。兩個測試都與 TestContainers 啟動的 MongoDB 資料庫進行互動。

2. 應用程式整合測試
應用程式整合測試用於測試不同應用程式元件之間的互動。我們將建立一個使用我們之前建立的資料訪問層的簡單應用程式,併為其編寫整合測試。

讓我們建立擴充套件AbstractBaseIntegrationTest類的應用程式整合測試類:

@AutoConfigureMockMvc
public class ProductIntegrationTest extends AbstractBaseIntegrationTest {
    @Autowired
    private MockMvc mvc;
    private ObjectMapper objectMapper = new ObjectMapper();
    <font>// ..<i>
}

我們需要@AutoConfigureMockMvc註釋來在我們的測試中啟用MockMvc支援,並需要MockMvc欄位對我們的應用程式執行 HTTP 請求。

現在,我們可以為我們的應用程式編寫整合測試:

@Test
public void givenProduct_whenSave_thenGetProduct() throws Exception {
    MvcResult mvcResult = mvc.perform(post(<font>"/products").contentType("application/json")
      .content(objectMapper.writeValueAsString(new Product(
"Banana", "Fruit", 10))))
      .andExpect(status().isOk())
      .andReturn();
    String productId = mvcResult.getResponse()
      .getContentAsString();
    mvc.perform(get(
"/products/" + productId))
      .andExpect(status().isOk());
}

我們開發了一個測試來儲存產品,然後使用 HTTP 檢索它。此過程涉及將資料儲存在 MongoDB 資料庫中,該資料庫由 TestContainers 初始化。

相關文章