简体   繁体   中英

How can I persist entities during a SpringBootTest integration test

I am writing an integration test for a SpringBoot 2 RestController. I want to test 404 behaviour and creation of entities. However, when I try to create entities and persist them before or during a test, they are not persisted in the SpringBoot context. By that I mean they are visible in the test context (during debugging of the test) but not for the Controller (ie it does not find them and my tests fail). What am I doing wrong?

How can I persist entities and flush the context during a test so that code that is called during an integration test sees them? I don't want to use a @before annotation to populate a database because I want to do it in my @test methods.

Here is my code. Thanks

@RunWith(SpringRunner.class)
@Transactional
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class InvoiceControlllerIT extends GenericControllerIT {

  @Autowired
  EntityManager entityManager;

  @Test
  @Transactional
  public void cascadesChildEntityAssociationOnCreate() throws IOException {
    assertThat(invoicerRepository.count(), equalTo(0L));
    assertThat(invoiceRepository.count(), equalTo(0L));
    assertThat(invoiceeRepository.count(), equalTo(0L));
    // create an invoicee
    Invoicee savedInvoicee = invoiceeRepository.save(new Invoicee());
    assertThat(invoiceeRepository.count(), equalTo(1L));
    // create an invoicer
    Invoicer savedInvoicer = invoicerRepository.save(new Invoicer());
    assertThat(invoicerRepository.count(), equalTo(1L));
    // THIS IS THE PROBLEM, FLUSHING DURING THE TEST DOES NOT EFFECT THE CONTROLLERS ABILITY TO SEE THE NEWLY CREATED ENTITIES
    entityManager.flush();
    // create input
    InvoiceInputDto inputDto = InvoiceInputDto
            .builder()
            .invoicee(savedInvoicee.getId())
            .invoicer(savedInvoicer.getId())
            .name("test-name")
            .build();
    // make call
    ResponseEntity<InvoiceDto> response = template.postForEntity(url("/invoices", TOKEN), inputDto, InvoiceDto.class);
    assertThat(response.getStatusCode(), equalTo(HttpStatus.CREATED));
    assertThat(response.getBody().getName(), equalTo(inputDto.getName()));
    // check associations
    assertThat(invoiceeRepository.findById(savedInvoicee.getId()).get().getInvoices(), hasSize(1));
  }
}

According to the docs:

If your test is @Transactional, it rolls back the transaction at the end of each test method by default. However, as using this arrangement with either RANDOM_PORT or DEFINED_PORT implicitly provides a real servlet environment, the HTTP client and server run in separate threads and, thus, in separate transactions. Any transaction initiated on the server does not roll back in this case

(source: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications )

Since the test transaction is separate from the HTTP server transaction, the controller won't see changes made from within the test method until the test transaction is actually committed. Conversely, you won't be able to roll back changes made as a result to the server call.

You will seriously make your life easier by providing a mock implementation for whatever service/repository your controller uses. Alternatively, you could use a tool like DBUnit to setup and tear down the database around each test case.

This worked for me:

@Inject
private EntityManagerFactory entityManagerFactory;

@BeforeEach
void setUp() {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();
    entityManager.persist(someEntity());
    entityManager.getTransaction().commit();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM