I want to test whether the FooRepository.lock()
works, after someone called the lock()
, others calling it should be wait.
The following is my best try, it doesn't work. I believe the reason is both entityManger
and fooRepository
are participating in the same transaction.
How to call the lock()
from another transaction? Or any suggestion for unit-testing the pessimistic lock? Thanks!!
FooRepositoryTest:
package com.example.demo;
import java.util.UUID;
import javax.persistence.LockModeType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@DataJpaTest
public class FooRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private FooRepository fooRepository;
@Test
public void lockTest() {
// given
Foo foo = new Foo();
foo.setName("foo-name");
UUID fooId = fooRepository.save(foo).getFooId();
entityManager.flush();
entityManager.clear();
// when
Foo savedFoo = fooRepository.findOne(fooId);
fooRepository.lock(savedFoo);
// then
// I want something like this to be lock wait,
// something to confirm the above fooRepository.lock() work
entityManager.getEntityManager().lock(savedFoo, LockModeType.PESSIMISTIC_WRITE);
}
}
class Foo:
package com.example.demo;
import java.util.UUID;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Foo {
@Id
@GeneratedValue
private UUID fooId;
private String name;
public UUID getFooId() {
return fooId;
}
public void setFooId(UUID fooId) {
this.fooId = fooId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class FooApplication:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FooApplication {
public static void main(String[] args) {
SpringApplication.run(FooApplication.class, args);
}
}
class FooRepository:
package com.example.demo;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
public interface FooRepository extends JpaRepository<Foo, UUID>, FooRepositoryCustom {
}
class FooRepositoryCustom:
package com.example.demo;
public interface FooRepositoryCustom {
public void lock(Foo foo);
}
class FooRepositoryImpl:
package com.example.demo;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import org.springframework.beans.factory.annotation.Autowired;
public class FooRepositoryImpl implements FooRepositoryCustom {
@Autowired
private EntityManager entityManager;
@Override
public void lock(Foo foo) {
entityManager.lock(foo, LockModeType.PESSIMISTIC_WRITE);
}
}
Using the "test-oracle", "test-mysql" or "test-postgresql" profile for your integration tests will allow you to run all pessimistic locking handling tests against your production database. 🔔 This format is suitable for the local environment when you do changes to your pessimistic locking handling.
In order to use optimistic locking, we need to have an entity including a property with @Version annotation. While using it, each transaction that reads data holds the value of the version property. Before the transaction wants to make an update, it checks the version property again.
You are getting unit testing wrong.
You do not write unit test to exercise functionality implemented by some 3rd party framework. Unit tests are for your units!
In other words: you do not need to verify that locking works as expected. Your unit does:
entityManager.lock(foo, LockModeType.PESSIMISTIC_WRITE);
so the only thing you could consider testing here: making sure that the entity manager lock()
method is called with the expected parameters.
Meaning: verify that your code does use the framework as you think it should be used - but don't get into testing other peoples code! You see - what would you do when your unit test shows that the framework is wrong ... you can't change that! (sure, you can write a bug report then)
Disclaimer: there might be special situations where you assume that some 3rd party product has a bug - then it might be very useful to write a unit test to test this assumption. So that you can later run the unit test against a new version of that product to see if the bug is still present.
Testing pessimistic locking handling with integration test is important (may I say mandatory?) for two reasons:
In the blog post Testing Pessimistic Locking Handling With SpringBoot and JPA you could:
🔔 The Spring Framework is used only as an example. You could easly adapt the tests for every other framework.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With