我进行了以下测试,生成带有公司 ID 和电子邮件的邀请。然后,我们使用相同的公司 ID 和电子邮件生成第二个。这应该违反数据库的唯一约束(请参阅邀请实体)。事实是,测试通过了,如下所示(我猜第二次插入时永远不会提交事务)
如果我在第二次插入后执行 findAll()
(请参阅下面的注释),我会在 findAll()
是索引的行上得到一个异常发生违规。
如何让 UserService
中的方法在方法返回时提交其事务,以便测试代码按原样抛出异常?
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserServiceTest {
... Everything is autowired
@Test
public void testCreateInvitation() {
String email = "test2@example.com";
Company company = userService.createCompany("ACMETEST", tu);
assertTrue(invitationRepository.count() == 0l);
assertNotNull(userService.sendInvitation(company, email));
assertTrue(invitationRepository.count() == 1l);
assertTrue(invitationRepository.findAllByEmail(email).size() == 1);
// Second should throw exception
userService.sendInvitation(company, email);
// If this line is uncommented, I get key violation exception, otherwise, test passes!!!!????
//Iterable<Invitation> all = invitationRepository.findAll();
}
邀请类别和约束:
@Entity
@Table(uniqueConstraints=@UniqueConstraint(columnNames={"email", "company_id"}))
@JsonIdentityInfo(generator= ObjectIdGenerators.PropertyGenerator.class, property="id")
@Data
public class Invitation {
@Id
@GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
String id;
@ManyToOne
@JoinColumn(name="company_id")
private Company company;
String email;
Date inviteDate;
Date registerDate;
}
用户服务:
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class UserService {
public Invitation sendInvitation(Company company, String toEmail) {
Invitation invitation = new Invitation();
invitation.setCompany(company);
invitation.setInviteDate(new Date());
invitation.setEmail(toEmail);
try {
// TODO send email
return invitationRepository.save(invitation);
} catch (Exception e) {
LOGGER.error("Cannot send invitation: {}", e.getMessage());
}
return null;
}
}
最佳答案
How do I make the methods in the UserService commit their transactions when the method returns, so the test code will throw an exception as-is?
自 @DataJpaTest
是 @Transactional
本身。代码写在 @Test
方法在事务内执行。您可以使用 Propogation.NOT_SUPPORTED 更改它其中
Execute non-transactionally, suspend the current transaction if one exists
@Test
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testCreateInvitation() {
// your test method body unchanged
}
使用这个你应该在第二个 userService.sendInvitation(company, email);
上得到一个异常打电话。
还有一些其他选项。
选项 1:
添加@Commit
您的测试方法。
所有测试都标有 @Transactional
是rolled back执行后。但 Hibernate 有一个微妙的问题:它可能会延迟执行 sql,直到您提交事务(或显式调用 flush
(选项 2))。
由于事务回滚,因此没有执行任何插入操作,因此不存在约束违规。
请注意使用@Commit
您将在测试方法外部抛出异常。
选项 2:
使用手册flush()
:
如果您的存储库 extends JpaRepository<>
:
@Test
public void testCreateInvitation() {
// create first invitation,
// create second invitation
invitationRepository.flush();
}
否则注入(inject)EntityManager
并调用flush()
关于它:
@PersistenceContext
private EntityManager em;
@Test
public void testCreateInvitation() {
// create first invitation,
// create second invitation
em.flush();
}
当 invitationRepository.flush()
时,您将收到异常或em.flush()
已调用。
选项 3: 使用 GenerationType.IDENTITY:
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
Hibernate 必须将 id 分配给 @Entity
在你之后save()
它。但如果不执行insert
,它就无法获取实体的ID。 .
附注 有件事让我困惑。
您使用@Transactional(propagation = Propagation.REQUIRES_NEW)
并且它应该在方法执行后强制提交事务。请检查您的日志以查看交易是否真正有效。
关于java - 测试期间的 Spring Boot JPA 事务 - 在插入时不抛出关键违规异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52237602/