Spring Boot JPA Transaction во время теста - исключение исключения ключа при вставке - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть следующий тест, который генерирует приглашение с идентификатором компании и электронным письмом.Затем мы создаем секунду с тем же идентификатором компании и адресом электронной почты.Это должно нарушать уникальное ограничение на БД (см. Раздел Приглашение).Дело в том, что тест проходит, как указано ниже (транзакция никогда не фиксируется для второй вставки, я думаю)

Если я сделаю findAll() после второй вставки (см. Комментарий ниже), я получу исключениев строке, где findAll() означает, что произошло нарушение индекса.

Как заставить методы в UserService зафиксировать свои транзакции при возврате метода, поэтому тестовый код выдаст исключение как-is?

@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 Ответ

0 голосов
/ 09 сентября 2018

Как заставить методы в UserService фиксировать свои транзакции при возврате метода, чтобы тестовый код генерировал исключение как есть?

Поскольку @DataJpaTest само по себе @Transactional. Код, написанный в методе @Test, выполняется внутри транзакции. Вы можете изменить его, используя Propogation.NOT_SUPPORTED which

Выполнить без транзакций, приостановить текущую транзакцию, если она существует

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

Используя это, вы должны получить исключение при втором userService.sendInvitation(company, email); вызове.

Также есть несколько других опций.

Вариант 1:

Добавьте @Commit к вашему методу испытаний.

Все тесты, отмеченные @Transactional, откатываются после его выполнения. Но с Hibernate есть одна тонкая вещь: он может отложить выполнение sql, пока вы не совершите транзакцию (или не вызовете flush явно (вариант 2)).

Поскольку транзакция откатана, вставки не выполняются, следовательно, нет нарушения ограничения.

Помните, что с помощью @Commit вы получите исключение вне вашего метода испытаний.

Вариант 2:

Руководство по эксплуатации flush():

Если ваш репозиторий extends JpaRepository<>:

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

В противном случае введите 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 должен назначить идентификатор для @Entity после того, как вы save() его. Но он не может получить идентификатор для сущности без выполнения insert.

приписка Меня смущает одна вещь.

Вы используете @Transactional(propagation = Propagation.REQUIRES_NEW), и это должно заставить совершить транзакцию после выполнения метода. Пожалуйста, проверьте ваши журналы, чтобы увидеть, если транзакции действительно работают.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...