0
@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 test:

@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 is a interface which I use to define Repository. Probably I Need stub the request and return test data? What is the proper wya I stub the request? I get

Field transactionRepository in org.restapi.PaymentTransactionsController required a bean of type 'org.backend.repo.PaymentTransactionRepository' that could not be found.
Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
Peter Penzov
  • 1,126
  • 134
  • 430
  • 808

2 Answers2

0

You can use a @MockBean to make a stub of repository in the application context:

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

Also, you can change the scope of tests to DataJpaTest or use a whole application context and run the test with prepared data in a real database. In this case, you can test more than controller logic, you will testing whole system logic.

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

  @Autowired
  private PaymentTransactionRepository transactionRepository;

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

To run this test case you need a database configuration for tests. If you don't use native queries in your application you can use h2. But if you intensive use native queries and a something special for your target database, you can use TestContainers library to run your real database image in docker and run tests on this image.

  • Can you please extend the test a with example for DataJpaTest? – Peter Penzov Dec 12 '18 at 08:25
  • I'm updated my answer. If you need more examples you can find it [here](https://github.com/antkorwin/damage-tests/blob/master/src/test/java/com/antkorvin/damagetests/repositories/RocketRepositoryIT.java) – Anatoliy Korovin Dec 13 '18 at 23:05
0

Controller Integration Test

About Integration Testing from the controller context, if your goal is performing integration testing involving the actual data source acceded by: PaymentTransactionRepository.

Problem:

In order to solve:

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

Or something equivalent, like:

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

Replace:

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

By:

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

Since:

SpringBootTest: Automatically searches for a @SpringBootConfiguration when nested @Configuration is not used, and no explicit classes are specified.

See Spring Docs

DataJpaTest Integration Test

As you asked recently, a sample testing DataJpaTest assuming that findByIdmethod of PaymentTransactionRepository class returns an Optional<DTO> would look like:

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

Functional Example

Please find here a simple example, 100% functional, that is pre-loading test data by using a H2 in-memory database and contains the minimum required gradle/spring configuration in order to pass the tests. It includes 2 types of integration tests using SprinbBoot2 (JPA) and JUnit5, 1) controller: ApiDocumentationJUnit5IntegrationTest and 2) repository: PaymentTransactionRepositoryIntegrationTest.

emecas
  • 1,586
  • 3
  • 27
  • 43
  • I get java.lang.IllegalStateException: Unable to find a SpringBootConfiguration, you need to use ContextConfiguration or SpringBootTest(classes=...) with your test – Peter Penzov Dec 14 '18 at 21:12
  • I'm wondering about what specific versions are you using (springboot, junit, gradle?) , may you provided your build file and full exception text? – emecas Dec 14 '18 at 21:31
  • pom file: https://pastebin.com/mHR0Tpfa and exception https://pastebin.com/zXw28TNu – Peter Penzov Dec 14 '18 at 22:05
  • Thanks, your `pom.xml` works for me, just adding com.h2database h2 test – emecas Dec 15 '18 at 01:07
  • I'm running out of options, I could not reproduce your exception by using `@SpringBootTest` without classes. 1) is you database configuration on place? were you able to tests `PaymentTransactionRepositoryIntegrationTest` independently? 2) what version of maven are you using? 3) are you using an IDE or the command-line? – emecas Dec 17 '18 at 18:05