Spring boot test @Transactional not saving

社会主义新天地 提交于 2019-12-18 06:53:40

问题


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 saving data, I think I have a problem with spring contexts ...

This is my Entity:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    @Id
    private int id;
    private String name;
}

This is the Person repository:

@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}

The Person service:

@Service
public class PersonService {

    @Autowired
    private PersonRepository repository;

    public Person createPerson(int id,String name) {
       return repository.save(new Person(id, name));
    }

    public List<Person> getPersons() {
      return repository.findAll();
    }
}

The Person Controller:

@RequestMapping
@RestController
public class PersonController {

  @Autowired
  private PersonService personService;

  @RequestMapping("/persons")
  public List<Person> getPersons() {
      return personService.getPersons();
  }

}

The main Application class:

@SpringBootApplication
public class BootIntegrationTestApplication {

  public static void main(String[] args) {
    SpringApplication.run(BootIntegrationTestApplication.class, args);
  }
}

The application.properties file:

spring.datasource.url= jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true

And the Test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BootIntegrationTestApplicationTests {

    @Autowired
    private PersonService personService;
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    @Transactional
    public void contextLoads() {
        Person person = personService.createPerson(1, "person1");
        Assert.assertNotNull(person);

        ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
    }
}

The test does not work, because the service is not saving the Person entity .... Thanks in advance


回答1:


@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.




回答2:


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




回答3:


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



回答4:


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



回答5:


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
}


来源:https://stackoverflow.com/questions/43658630/spring-boot-test-transactional-not-saving

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!