简体   繁体   English

Spring Boot 测试@Transactional 不保存

[英]Spring boot test @Transactional not saving

I'am trying to do a simple Integration test using Spring Boot Test in order to test the e2e use case.我正在尝试使用 Spring Boot Test 进行简单的集成测试,以测试 e2e 用例。 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...我的测试不起作用,因为我无法使存储库保存数据,我认为我的 spring 上下文有问题......

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: application.properties 文件:

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测试不起作用,因为服务没有保存 Person 实体....提前致谢

@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.参考@Rollback(false)是避免回滚的关键。

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:感谢 M. Deinum,我想我明白了,所以最好的办法是将测试逻辑分成两个测试,第一个将只测试服务(因此这个可以是事务性的),第二个是控制器:

Test 1:测试 1:

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

Test 2:测试 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);
}

Spring for saving entity requires transaction.用于保存实体的 Spring 需要事务。 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);
        }
    });        
}

For each @Test function that makes a DB transaction, if you want to permanently persist the changes, then you can use @Rollback(false)对于每个进行数据库事务的@Test函数,如果你想永久保留更改,那么你可以使用@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:我使用 jHipster 生成的 SpringBoot 项目对其进行了测试:

  • SpringBoot: 1.5.4弹簧引导:1.5.4
  • jUnit 4.12 jUnit 4.12
  • Spring 4.3.9春季 4.3.9

Do not use @Rollback(false) .不要使用@Rollback(false) Unit Test should not generate data.单元测试不应生成数据。

JPA FlushMode is AUTO (default - flush INSERT/UPDATE/DELETE SQL when query occurs) / COMMIT . JPA FlushMode 是AUTO (默认 - 查询发生时刷新 INSERT/UPDATE/DELETE SQL)/ COMMIT

Just query the working entity for forcing FLUSH, or using EntityManager to force flush只需查询工作实体强制刷新,或使用 EntityManager 强制刷新

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

Pay your attention to the order in which the tests are executed, the tests with the @Commit or @Rollback(false) annotation must be executed first: https://www.baeldung.com/junit-5-test-order请注意测试执行的顺序,带有@Commit 或@Rollback(false) 注释的测试必须首先执行: https ://www.baeldung.com/junit-5-test-order

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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