2

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

2
  • 1
    Note that @DataJpaTest is itself annotated with @Transactional causing the whole test to run in a single transaction. Try using @SpringBootTest to see if that makes a difference. Commented Oct 14 at 20:11
  • Inject the TestEntityManager into your class and at the point you would expect a commit from the transaction call flush on it. Commented Oct 15 at 5:45

1 Answer 1

0

Thanks to @jaco0646:

Note that @DataJpaTest is itself annotated with @Transactional causing the whole test to run in a single transaction. Try using @SpringBootTest to see if that makes a difference.

– jaco0646

Infact with @SpringBootTest it works, basically the @DataJpaTest are itself @Transactional, so it performes rollback only at the end of the test, not at the end of the method orderService.insertNewOrder.

Sign up to request clarification or add additional context in comments.

1 Comment

Alternatively, it might be possible to use @Transactional(NESTED) in the class to test but that would be a change of behavior.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.