I have a Spring-JUnit test with a setup method that uses JPA Entities to setup test data. Within the test itself, legacy code uses JDBC-Templates to query the inserted test data. When the legacy code issues a query, the call to jdbcTemplate.query(...) hangs.
My Unit Test looks like this:
#TransactionConfiguration(defaultRollback = false)
#ContextConfiguration(locations = { "/testContext.xml" })
#Transactional
public class MyTest {
#PersistenceContext(unitName = "someUnit")
private EntityManager entityManager;
#Test
public void test() {
// here some legacy Code is called that uses JDBC Templates to query
// the inserted test data. The legacy code hangs upon jdbcTemplate.query(...)
}
#Before
public void before() {
this.entityManager.persist(new Entity1(...));
this.entityManager.persist(new Entity2(...));
}
}
My question is: Why doesn't the enitymanager commit upon exiting the before() method? Or does it commit and immediately start a new transaction, that still references the stored entity? I also tried to do without the junit annotation #Before annotation and manually call the before() method. But this gives the same result.
Any advice is highly appreciated.
Test class
#ContextConfiguration(locations = { "/testContext.xml" })
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
public class MyTest extends AbstractTransactionalJUnit4SpringContextTests{
#PersistenceContext(unitName = "someUnit")
private EntityManager entityManager;
#Autowired
private BeforeService beforeService;
#Test
public void test() {
beforeService.before();
// here some legacy Code is called that uses JDBC Templates to query
// the inserted test data. The legacy code hangs upon jdbcTemplate.query(...)
}
}
Before Service
public class BeforeService {
private EntityManager entityManager;
#PersistenceContext
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void before() {
entityManager.persist(new Entity1(...));
entityManager.persist(new Entity2(...));
}
}
I hope I've given you all the answers about your question.
Related
I have a service with a persistence setup using JPA, Hibernate and Guice (if it's useful, I'm not using Spring). This is the first, working version of my code:
public class BookDao {
#Inject
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) {
getEntityManager().persist(book);
}
}
public class MyAppModule extends AbstractModule {
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
final JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
But now I need to configure multiple persistence units. I'm following the advice in this mailing list, and according to them, I should move my module logic to a private module. I did as suggested and created a second version of the same code, the changes are commented below:
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ FIELD, PARAMETER, METHOD })
public #interface ProductionDataSource {} // defined this new annotation
public class BookDao {
#Inject
#ProductionDataSource // added the annotation here
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) throws Exception {
getEntityManager().persist(book);
}
}
public class MyAppModule extends PrivateModule { // module is now private
#Override
protected void configure() {
initializePersistence();
// expose the annotated entity manager
Provider<EntityManager> entityManagerProvider = binder().getProvider(EntityManager.class);
bind(EntityManager.class).annotatedWith(ProductionDataSource.class).toProvider(entityManagerProvider);
expose(EntityManager.class).annotatedWith(ProductionDataSource.class);
}
private void initializePersistence() {
JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
The newly annotated EntityManager is being correctly injected by Guice and is non-null, but here's the fun part: some of my unit tests started failing, for example:
class BookDaoTest {
private Injector injector;
private BookDao testee;
#BeforeEach
public void setup() {
injector = Guice.createInjector(new MyAppModule());
injector.injectMembers(this);
testee = injector.getInstance(BookDao.class);
}
#Test
public void testPersistBook() throws Exception {
// given
Book newBook = new Book();
assertNull(newBook.getId());
// when
newBook = testee.persist(newBook);
// then
assertNotNull(newBook.getId()); // works in the first version, fails in the second
}
}
In the first version of my code the last line above just works: the entity is persisted and has a new id. However, in the second version of my code (using a PrivateModule and exposing an annotated EntityManager from it) the persist() operation doesn't work anymore, the entity is without an id. What could be the problem? I didn't do any other configuration changes in my environment, and I don't see error messages in the logs. Let me know if you need more details.
It turns out that the problem was the #Transactional annotation. In the first version of my code, Guice automatically adds interceptors for managing the transaction. By doing a debug, I found out that before executing my persist(Book book) method, Guice calls the following method from the com.google.inject.internal.InterceptorStackCallback package:
public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
In the second version of my code, when I exposed the persistence unit from a private module the above interceptor was no longer called, leaving my persist operation without transaction handling. This is a known issue and is by design.
As a workaround I had to implement transactions by hand, making my code more verbose. I also had to change the way the entity manager is injected. This solution worked for me:
public class BookDao {
#Inject
#Named(PROD_PERSISTENCE_UNIT_NAME)
private EntityManagerFactory entityManagerFactory;
private EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
public void persist(Book book) throws Exception {
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.persist(book);
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw e;
} finally {
em.close();
}
}
}
public class MyAppModule extends PrivateModule {
public static final String PROD_PERSISTENCE_UNIT_NAME = "prod";
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
// persistence unit set to prod DB
final JpaPersistModule jpaPersistModule = new JpaPersistModule(PROD_PERSISTENCE_UNIT_NAME);
// connection properties set to suitable prod values
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
// expose bindings to entity manager annotated as "prod"
bind(JPAInitializer.class).asEagerSingleton();
bind(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).to(PersistService.class).asEagerSingleton();
expose(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManagerFactory.class));
expose(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManager.class));
expose(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(UnitOfWork.class));
expose(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
}
}
As a lesson, be very watchful around annotations and other such "magic" that modifies your code under the hood, finding bugs becomes quite difficult.
I'am trying to do a simple Integration test using Spring Boot Test in order to test the e2e use case. My test does not work because I'am not able to make the repository saving data, I think I have a problem with spring contexts ...
This is my Entity:
#Entity
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Person {
#Id
private int id;
private String name;
}
This is the Person repository:
#Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
The Person service:
#Service
public class PersonService {
#Autowired
private PersonRepository repository;
public Person createPerson(int id,String name) {
return repository.save(new Person(id, name));
}
public List<Person> getPersons() {
return repository.findAll();
}
}
The Person Controller:
#RequestMapping
#RestController
public class PersonController {
#Autowired
private PersonService personService;
#RequestMapping("/persons")
public List<Person> getPersons() {
return personService.getPersons();
}
}
The main Application class:
#SpringBootApplication
public class BootIntegrationTestApplication {
public static void main(String[] args) {
SpringApplication.run(BootIntegrationTestApplication.class, args);
}
}
The application.properties file:
spring.datasource.url= jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
And the Test:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BootIntegrationTestApplicationTests {
#Autowired
private PersonService personService;
#Autowired
private TestRestTemplate restTemplate;
#Test
#Transactional
public void contextLoads() {
Person person = personService.createPerson(1, "person1");
Assert.assertNotNull(person);
ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
}
}
The test does not work, because the service is not saving the Person entity ....
Thanks in advance
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
public class SmokeTest {
#Autowired
UserController userController;
#Autowired
UserDao userDAO;
#Rollback(false) // This is key to avoid rollback.
#Test
public void contextLoads() throws Exception {
System.out.println("Hiren");
System.out.println("started");
userDAO.save(new User("tyx", "x#x.com"));
}
}
Refer #Rollback(false) is key to avoid rollback.
Thanks to M. Deinum, I think I get the point,
So the best is to separate the logic of the test into two tests, the first will testing just the service (so this one could be transactional) and the second the controller:
Test 1:
#Test
#Transactional
public void testServiceSaveAndRead() {
personService.createPerson(1, "person1");
Assert.assertTrue(personService.getPersons().size() == 1);
}
Test 2:
#MockBean
private PersonService personService;
#Before
public void setUp() {
//mock the service
given(personService.getPersons())
.willReturn(Collections.singletonList(new Person(1, "p1")));
}
#Test
public void testController() {
ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
Assert.assertTrue(persons.getBody()!=null && persons.getBody().length == 1);
}
Spring for saving entity requires transaction. But until transaction has been commited changes not be visible from another transaction.
Simplest way is call controller after commit transaction
#Test
#Transactional
public void contextLoads() {
Person person = personService.createPerson(1, "person1");
Assert.assertNotNull(person);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
ResponseEntity<Person[]> persons = restTemplate.getForEntity("/persons", Person[].class);
}
});
}
For each #Test function that makes a DB transaction, if you want to permanently persist the changes, then you can use #Rollback(false)
#Rollback(false)
#Test
public void createPerson() throws Exception {
int databaseSizeBeforeCreate = personRepository.findAll().size();
// Create the Person
restPersonMockMvc.perform(post("/api/people")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(person)))
.andExpect(status().isCreated());
// Validate the Person in the database
List<Person> personList = personRepository.findAll();
assertThat(personList).hasSize(databaseSizeBeforeCreate + 1);
Person testPerson = personList.get(personList.size() - 1);
assertThat(testPerson.getFirstName()).isEqualTo(DEFAULT_FIRST_NAME);
assertThat(testPerson.getLastName()).isEqualTo(DEFAULT_LAST_NAME);
assertThat(testPerson.getAge()).isEqualTo(DEFAULT_AGE);
assertThat(testPerson.getCity()).isEqualTo(DEFAULT_CITY);
}
I tested it with a SpringBoot project generated by jHipster:
SpringBoot: 1.5.4
jUnit 4.12
Spring 4.3.9
Pay your attention to the order in which the tests are executed, the tests with the #Commit or #Rollback(false) annotation must be executed first: https://www.baeldung.com/junit-5-test-order
Do not use #Rollback(false). Unit Test should not generate data.
JPA FlushMode is AUTO (default - flush INSERT/UPDATE/DELETE SQL when query occurs) / COMMIT.
Just query the working entity for forcing FLUSH, or using EntityManager to force flush
#Test
public void testCreate(){
InvoiceRange range = service.createInvoiceRange(1, InvoiceRangeCreate.builder()
.form("01GTKT0/010")
.serial("NV/18E")
.effectiveDate(LocalDate.now())
.rangeFrom(1L)
.rangeTo(1000L)
.build(), new byte[] {1,2,3,4,5});
service.findByCriteria(1, "01GTKT0/010", "NV/18E"); // force flush
// em.flush(); // another way is using entityManager for force flush
}
I am using: Netbeans, Glassfish, Mockito
While working with Java EE for the first time I have come across the issue of trying to test methods that interact with the database using an entitymanager.
In the below code snippets I have tried to mock out the entity manager so i know that the db interaction will not be tested exactly and for this testing thats ok. But I am striggling on instansiating the UsersController because it has an injected EntityManager which is always null. I would like the EntityManager to be mocked out so I can test the rest of the method.
Below is a class that interacts with the db. NOTE this is an example methods, it is not for use in production.
#Stateless
public class UsersController {
#PersistenceContext()
EntityManager em;
public User getOne() {
em.getEntityManagerFactory().getCache().evictAll();
User theUser = null;
try {
Query q = em.createNamedQuery("User.findAll");
Collection<User> entities = q.getResultList();
theUser = Iterables.get(entities, 1);
}
catch(NoResultException e){}
em.flush();
return theUser;
};
}
The test file that will test this method.
#RunWith(MockitoJUnitRunner.class)
public class UsersControllerTest {
#Mock
private UsersController usersController;
#Mock
private EntityManager entityManagerMock;
private Collection<User> mockUsersDbCollection = //...
#BeforeClass
public void setUpClass() {
when(entityManagerMock.createNamedQuery("User.findAll")).thenReturn(mockUsersDbCollection);
}
#Test
public void findOneTest(){
User mockUserDbEntry = new User("1", "pa$$word", "salt", "user1", "user1#email.com", false);
User returnedUser = null;
returnedUser = usersController.getOne();
assertEquals(returnedUser.getId(), "1");
}
}
Whenever the UsersController mock is created the entityManager is always null causing issues, how can I inject the EntityManagerMock so it will work?
You have a few issues here:
you are testing your UsersController, therefore it should not be mocked; rather you should use the #InjectMocks annotation on it because you want Mockito to inject your mock entity manager
the #BeforeClass annotation only works when applied to a static method, which in turn can only access static instance variables; in this instance I think you just need the #Before annotation
your entityManagerMock.createNamedQuery needs to return a mock Query object, the code you pasted should not even compile
In the end, your test should look something like:
#RunWith(MockitoJUnitRunner.class)
public class UsersControllerTest {
#InjectMocks
private UsersController usersController;
#Mock
private EntityManager entityManagerMock;
#Mock
private EntityManagerFactory entityManagerFactory;
#Mock
private Cache emfCache;
#Mock
private Query findAllQuery;
private List<User> mockUsersDbCollection = //...
#Before
public void setUp() {
when(entityManagerFactory.getCache())
.thenReturn(emfCache);
when(entityManagerMock.getEntityManagerFactory())
.thenReturn(entityManagerFactory);
when(entityManagerMock.createNamedQuery("User.findAll"))
.thenReturn(findAllQuery);
when(findAllQuery.getResultList())
.thenReturn(mockUsersDbCollection);
}
#Test
public void findOneTest(){
mockUsersDbCollection.add(new User("1", "pa$$word", "salt", "user1", "user1#email.com", false));
User returnedUser = usersController.getOne();
assertEquals(returnedUser.getId(), "1");
}
}
I am trying to test my PreUpdateEventListener flow, but I cannot seem to make it work in the JUnit tests. I am not getting any error, but the code is not called.
My PreUpdateEventListener:
#Component
public class CandidateListener implements PreUpdateEventListener {
#Autowired
EntityManagerFactory entityManagerFactory;
#PostConstruct
private void init() {
HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) this.entityManagerFactory;
SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory();
EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
registry.appendListeners(EventType.POST_LOAD, this);
registry.appendListeners(EventType.PRE_UPDATE, this);
}
#Override
public boolean onPreUpdate(PreUpdateEvent event) {
final Object entity = event.getEntity();
if (entity == null) return false;
// code here not being called in unit tests, but works fine on server
return false;
}
}
The test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#IntegrationTest
#Transactional
public class CandidateListenerTest {
#Autowired
CandidateRepository candidateRepository;
#Autowired
EntityAuditEventRepository entityAuditEventRepository;
#Autowired
SessionFactory sessionFactory;
#Test
public void testHistoryLogging() {
Candidate cand = new Candidate();
cand.setEmail("123#gmail.com");
cand.setFirstName("12");
cand.setLastName("3");
cand = candidateRepository.save(cand);
cand.setLastName("34");
candidateRepository.save(cand);
assertEquals(entityAuditEventRepository.findAll().size(), 1);
}
}
I have tried injecting the SessionFactory into the test and calling SessionFactory#flush method, but that throws No CurrentContextSession error, which I cannot seem to fix.
Finally managed to fix this.
What I have kept trying to do was to inject the EntityManager and call flush from there, but there would have been no change in the actual output (the code was still not being called).
The solution for me was inspired from here making my final test look like this:
#Test
#Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testHistoryLogging() {
Candidate cand = new Candidate();
cand.setEmail("123#gmail.com");
cand.setFirstName("12");
cand.setLastName("3");
cand = candidateRepository.save(cand);
cand.setLastName("34");
candidateRepository.save(cand);
assertEquals(entityAuditEventRepository.findAll().size(), 1);
}
Basically every call is executed non-transactionally, which saves the candidate into the database after the save method from the repository is called.
The DAO is simply:
#PersistenceContext
private EntityManager em;
#Override
public List<TestModel> findAll() {
return em.createNamedQuery("TestModel.findAll").getResultList();
}
and the test is:
#Mock
private EntityManager em;
#InjectMocks
#Autowired
private TestModelDao dao = new TestModelDaoImpl();
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(dao);
}
and the mock test is:
#Test
public void testFindAll() {
when(dao.findAll()).thenReturn(testModelList());
List<TestModel> testModels = dao.findAll();
assertThat(testModels, is(testModelList()));
}
but the above test throws a NullPointerException. What am I doing wrong?
Mockito returns null when the method createNamedQuery is called so you get a NullpointerException on getResultList.
You have to
TypedQuery mockedQuery = mock(TypedQuery.class);
when(em.createNamedQuery(eq("TestModel.findAll")).thenReturn(mockedQuery);
when(mockedQuery.getResultList()).thenReturn(testModelList());
But like JB Nizet said you are testing the EntityManager which was already tested by the jpa provider.
I recommend you to create an Integration Test with dbunit and an in-memory database for testing your dao.