代码之家  ›  专栏  ›  技术社区  ›  mikeb

测试期间Spring引导JPA事务-插入时不引发密钥冲突异常

  •  1
  • mikeb  · 技术社区  · 6 年前

    我有下面的测试生成一个带有公司id和电子邮件的邀请。然后,我们用相同的公司id和电子邮件生成第二个。这应该违反DB上的唯一约束(请参见邀请实体)。问题是,测试通过了下面列出的测试(我想,事务永远不会为第二次插入提交)

    如果我做一个 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;
        }
    }
    
    1 回复  |  直到 6 年前
        1
  •  5
  •   Denis Zavedeev Brian Kelly    6 年前

    如何使UserService中的方法在方法返回时提交其事务,以便测试代码按原样抛出异常?

    @DataJpaTest @Transactional 它自己。编写的代码 @Test 方法在事务中执行。你可以用 Propogation.NOT_SUPPORTED 哪一个

    @Test
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void testCreateInvitation() {
        // your test method body unchanged
    }
    

    使用这个你应该会在第二天得到一个异常 userService.sendInvitation(company, email); 打电话。

    还有一些其他的选择。

    添加 @Commit

    标记为的所有测试 rolled back 在执行之后。但是Hibernate有一个微妙的地方:它可能会延迟执行sql,直到您提交事务(或调用) flush 明确(选项2))。

    由于事务回滚没有执行插入,因此没有违反约束。

    小心使用 @提交 外部 你的测试方法。

    方案2:

    使用说明书 flush() :

    如果您的存储库 extends JpaRepository<>

    @Test
    public void testCreateInvitation() {
        // create first invitation,
        // create second invitation
        invitationRepository.flush();
    }
    

    EntityManager 刷新() 在上面:

    @PersistenceContext
    private EntityManager em;
    
    @Test
    public void testCreateInvitation() {
        // create first invitation,
        // create second invitation
        em.flush();
    }
    

    你会得到一个例外当 invitationRepository.flush() em.flush()

    方案3:

    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;
    

    Hibernate必须将id分配给 @Entity 在你之后 save() insert .

    有一件事让我困惑。

    @Transactional(propagation = Propagation.REQUIRES_NEW)