Spring boot test @Transactional not saving

后端 未结 5 1377
别那么骄傲
别那么骄傲 2020-12-17 21:24

I\'am trying to do a simple Integration test using Spring Boot Test in order to test the e2e use case. My test does not work because I\'am not able to make the repository sa

相关标签:
5条回答
  • 2020-12-17 22:02

    Spring for saving entity requires transaction. But until transaction has been commited changes not be visible from another transaction.

    Simplest way is call controller after commit transaction

    @Test
    @Transactional
    public void contextLoads() {
        Person person = personService.createPerson(1, "person1");
        Assert.assertNotNull(person);
    
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
            }
        });        
    }
    
    0 讨论(0)
  • 2020-12-17 22:13

    For each @Test function that makes a DB transaction, if you want to permanently persist the changes, then you can use @Rollback(false)

    @Rollback(false)
    @Test
    public void createPerson() throws Exception {
        int databaseSizeBeforeCreate = personRepository.findAll().size();
    
        // Create the Person
        restPersonMockMvc.perform(post("/api/people")
            .contentType(TestUtil.APPLICATION_JSON_UTF8)
            .content(TestUtil.convertObjectToJsonBytes(person)))
            .andExpect(status().isCreated());
    
        // Validate the Person in the database
        List<Person> personList = personRepository.findAll();
        assertThat(personList).hasSize(databaseSizeBeforeCreate + 1);
        Person testPerson = personList.get(personList.size() - 1);
        assertThat(testPerson.getFirstName()).isEqualTo(DEFAULT_FIRST_NAME);
        assertThat(testPerson.getLastName()).isEqualTo(DEFAULT_LAST_NAME);
        assertThat(testPerson.getAge()).isEqualTo(DEFAULT_AGE);
        assertThat(testPerson.getCity()).isEqualTo(DEFAULT_CITY);
    }
    

    I tested it with a SpringBoot project generated by jHipster:

    • SpringBoot: 1.5.4
    • jUnit 4.12
    • Spring 4.3.9
    0 讨论(0)
  • 2020-12-17 22:17
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    public class SmokeTest {
    
        @Autowired
        UserController userController;
    
        @Autowired
        UserDao userDAO;
    
        @Rollback(false) // This is key to avoid rollback.
        @Test   
        public void contextLoads() throws Exception {
            System.out.println("Hiren");
    
            System.out.println("started");
            userDAO.save(new User("tyx", "x@x.com"));
        }
    }
    

    Refer @Rollback(false) is key to avoid rollback.

    0 讨论(0)
  • 2020-12-17 22:21

    Thanks to M. Deinum, I think I get the point, So the best is to separate the logic of the test into two tests, the first will testing just the service (so this one could be transactional) and the second the controller:

    Test 1:

    @Test
    @Transactional
    public void testServiceSaveAndRead() {
        personService.createPerson(1, "person1");
        Assert.assertTrue(personService.getPersons().size() == 1);
    }
    

    Test 2:

    @MockBean
    private PersonService personService;
    
    @Before
    public void setUp() {
        //mock the service
        given(personService.getPersons())
                .willReturn(Collections.singletonList(new Person(1, "p1")));
    }
    
    @Test
    public void testController() {
        ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
        Assert.assertTrue(persons.getBody()!=null && persons.getBody().length == 1);
    }
    

    Hope that it will help someone someday ... thanks for all of you

    0 讨论(0)
  • 2020-12-17 22:25

    Do not use @Rollback(false). Unit Test should not generate data.

    JPA FlushMode is AUTO (default - flush INSERT/UPDATE/DELETE SQL when query occurs) / COMMIT.

    Just query the working entity for forcing FLUSH, or using EntityManager to force flush

    @Test
    public void testCreate(){
        InvoiceRange range = service.createInvoiceRange(1, InvoiceRangeCreate.builder()
                .form("01GTKT0/010")
                .serial("NV/18E")
                .effectiveDate(LocalDate.now())
                .rangeFrom(1L)
                .rangeTo(1000L)
                .build(), new byte[] {1,2,3,4,5});
    
        service.findByCriteria(1, "01GTKT0/010", "NV/18E");  // force flush
        // em.flush(); // another way is using entityManager for force flush
    }
    
    0 讨论(0)
提交回复
热议问题