I'm testing that:
@RunWith(SpringRunner.class)
@DataJpaTest
@Import({ OrderService.class, UserService.class })
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class OrderServiceRepositoryIT {
@Autowired
private OrderService orderService;
@Autowired
private UserService userService;
@Autowired
private OrderRepository orderRepository;
@Test
public void testServiceInsertNewOrderWhenRepositorySaveFailsShouldRollbackUserBalance() {
User user = userService.insertNewUser(new User(null, "u1", "n1", "e1", 2000));
Order notSavedOrder = new Order(null, null, 500, user);
assertThatThrownBy(() -> orderService.insertNewOrder(notSavedOrder))
.isInstanceOf(DataIntegrityViolationException.class);
assertThat(orderRepository.findById(notSavedOrder.getId()).orElse(null)).isNull();
assertThat(userService.getUserById(user.getId()).getBalance()).isEqualTo(2000);
}
}
With this method from orderService:
@Transactional
public Order insertNewOrder(Order order) {
order.setId(null);
try {
userService.withdraw(order.getUser().getId(), order.getPrice());
} catch (IllegalArgumentException | IllegalStateException e) {
throw new IllegalStateException("Unable to insert new order");
}
return orderRepository.save(order);
}
And this one from userService:
@Transactional
public void withdraw(long id, long amount) {
if (amount < 0) {
throw new IllegalArgumentException("Withdraw amount cannot be negative");
}
User user = userRepository.findById(id).orElse(null);
if (user == null) {
throw new IllegalStateException("User not found");
}
if (user.getBalance() - amount < 0) {
throw new IllegalStateException("Not enough balance to perform withdraw");
}
user.setBalance(user.getBalance() - amount);
userRepository.save(user);
}
So I manually tested that and if I try to insert an order that fails on the userRepository.save(user); the rollback actually happens. But in the test it doesn't, as I receive this error:
expected: 2000L
but was: 1500L
Why?
I'm trying every possible scenarios, but the test does not pass
@DataJpaTestis itself annotated with@Transactionalcausing the whole test to run in a single transaction. Try using@SpringBootTestto see if that makes a difference.TestEntityManagerinto your class and at the point you would expect acommitfrom the transaction callflushon it.