java Spring引导测试@Transactional不保存
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43658630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring boot test @Transactional not saving
提问by Hedi Ayed
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 ...
我正在尝试使用 Spring Boot Test 进行简单的集成测试,以测试 e2e 用例。我的测试不起作用,因为我无法让存储库保存数据,我想我的 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 实体......提前致谢
回答by user2758406
@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", "[email protected]"));
}
}
Refer @Rollback(false)
is key to avoid rollback.
参考@Rollback(false)
是避免回滚的关键。
回答by Hedi Ayed
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);
}
Hope that it will help someone someday ... thanks for all of you
希望有一天它会帮助某人......感谢你们所有人
回答by Eduardo Toural
For each @Test
function that makes a DB transaction, if you want to permanently persist the changes, then you can use @Rollback(false)
对于每个@Test
进行 DB 事务的函数,如果要永久保留更改,则可以使用@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
- jUnit 4.12
- Spring 4.3.9
- 弹簧靴:1.5.4
- jUnit 4.12
- 春天 4.3.9
回答by Nick Savenia
Spring for saving entity requires transaction. But until transaction has been commited changes not be visible from another transaction.
用于保存实体的 Spring 需要事务。但是在提交事务之前,其他事务无法看到更改。
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);
}
});
}
回答by Neo Pham
Do not use @Rollback(false)
. Unit Test should not generate data.
不要使用@Rollback(false)
. 单元测试不应生成数据。
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
}