用数据库实施Rest测试

时间:2018-12-11 22:12:49

标签: java junit5 webmock junit-jupiter

@RestController
@RequestMapping("/transactions")
public class PaymentTransactionsController {

    @Autowired
    private PaymentTransactionRepository transactionRepository;

    @GetMapping("{id}")
    public ResponseEntity<?> get(@PathVariable String id) {
        return transactionRepository
                .findById(Integer.parseInt(id))
                .map(mapper::toDTO)
                .map(ResponseEntity::ok)
                .orElseGet(() -> notFound().build());
    }

JUnit 5测试:

@ExtendWith({ RestDocumentationExtension.class, SpringExtension.class })
@SpringBootTest(classes = PaymentTransactionsController.class)
public class ApiDocumentationJUnit5IntegrationTest {

    @Autowired
    private ObjectMapper objectMapper;

    private MockMvc mockMvc;

    @BeforeEach
    public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider restDocumentation) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                .apply(documentationConfiguration(restDocumentation)).build();
    }

    @Test
    public void uniqueTransactionIdLenght() {
        try {
            this.mockMvc.perform(RestDocumentationRequestBuilders.get("/transactions/1")).andExpect(status().isOk())
                    .andExpect(content().contentType("application/xml;charset=UTF-8"))
                    .andDo(document("persons/get-by-id"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

PaymentTransactionRepository是我用来定义存储库的接口。可能需要存根请求并返回测试数据吗?我存根请求的适当原因是什么?我得到

Field transactionRepository in org.restapi.PaymentTransactionsController required a bean of type 'org.backend.repo.PaymentTransactionRepository' that could not be found.

2 个答案:

答案 0 :(得分:0)

您可以使用@MockBean在应用程序上下文中创建存储库的存根:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = PaymentTransactionsController.class)
class ApiDocumentationJUnit5IntegrationTest {

  @MockBean
  private PaymentTransactionRepository transactionRepository;

  ...

  @BeforeEach
  void setUp() {
    when(transactionRepository.findById(eq(ID))).thenReturn(mock);
  }

  @Test
  void testSomething(){
     ...
  }

此外,您可以将测试范围更改为DataJpaTest或使用整个应用程序上下文,并使用真实数据库中准备好的数据运行测试。在这种情况下,您不仅可以测试控制器逻辑,还可以测试整个系统逻辑。

@DataJpaTest 
@ExtendWith(SpringExtension.class)
class ApiDocumentationJUnit5IntegrationTest {

  @Autowired
  private PaymentTransactionRepository transactionRepository;

  @Test
  void testSomething(){
     List<Payment> payments = transactionRepository.findAll();
     assertThat(payments).hasSize(3);
     ...
  }
}

要运行此测试用例,需要对测试进行数据库配置。如果您不在应用程序中使用本机查询,则可以使用h2。但是,如果您密集使用本机查询以及目标数据库的一些特殊功能,则可以使用TestContainers库在docker中运行真实的数据库映像,并在该映像上运行测试。

答案 1 :(得分:0)

控制器集成测试

关于从控制器上下文进行的集成测试,如果您的目标是执行涉及PaymentTransactionRepository所访问的实际数据源的集成测试。

问题:

为了解决:

Field transactionRepository in org.restapi.PaymentTransactionsController required a bean of type 'org.backend.repo.PaymentTransactionRepository' that could not be found.

或类似的东西,例如:

java.lang.IllegalStateException: Failed to load ApplicationContext

Caused by: 

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'paymentTransactionsController': Unsatisfied dependency expressed through field 'transactionRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.data.PaymentTransactionRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Caused by: 

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.data.PaymentTransactionRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

替换:

@ExtendWith({ RestDocumentationExtension.class, SpringExtension.class })
@SpringBootTest(classes = PaymentTransactionsController.class)
public class ApiDocumentationJUnit5IntegrationTest {

通过:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApiDocumentationJUnit5IntegrationTest {

自:

  

SpringBootTest:当不使用嵌套的@Configuration且未指定显式类时,自动搜索@SpringBootConfiguration。

请参见Spring Docs

DataJpaTest集成测试

最近您问过,假设findById类的PaymentTransactionRepository方法返回Optional<DTO>的示例DataJpaTest测试示例如下:

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest
public class PaymentTransactionRepositoryIntegrationTest {

    @Autowired
    PaymentTransactionRepository target;

    @BeforeEach
    public void setUp() throws Exception {
    }

    @Test
    public void testNotNull() {
        assertNotNull(target);
    }

    @Test
    public void testFindByIdFound() {
        Optional<DTO> res = target.findById(1L);
        assertTrue(res.isPresent());
    }

    @Test
    public void testFindByIdNotFound() {
        Optional<DTO> res = target.findById(3L);
        assertFalse(res.isPresent());
    }
}

功能示例

请找到here一个简单的示例,该示例具有100%的功能,即使用H2内存数据库预先加载测试数据,并且包含通过测试所需的最低gradle / spring配置。它包括使用SprinbBoot2(JPA)和JUnit5的2种类型的集成测试,1)控制器:ApiDocumentationJUnit5IntegrationTest和2)仓库:PaymentTransactionRepositoryIntegrationTest