Unable to Mock EntityManager Criteria API - java

I want to write the junit for EntityManager but unable to mock the EntityManager. My service class method listed below:
#PersistenceContext
private EntityManager em;
public List<Items> getItems() {
Order order = Order.desc("ASC");
Criteria crit = em.unwrap(Session.class).createCriteria(Items.class);
Criteria critRowCount = em.unwrap(Session.class).createCriteria(Items.class);
crit.add(Restrictions.eq("createdBy", "John"));
critRowCount.add(Restrictions.eq("createdBy", "John"));
crit.setFirstResult((pageNo - 1) * pageSize);
crit.setMaxResults(pageSize);
crit.addOrder(order);
List<Items> items = crit.list();
critRowCount.add(Restrictions.sqlRestriction("Query"));
critRowCount.setProjection(Projections.rowCount()).uniqueResult();
return items;
}
I used in my tast case:
#MockBean
private EntityManager em;
and when I called below method in service
List<Items> items = crit.list();
This will hit DB. Please halp me to resolve the problem.
Thanks in Advance!

I have no idea why the mocking does not work (from what you present here, I'd say it should work). But I would like to suggest another approach. I suppose the getItems() method resides in some kind of Repository or DAO or whatever you call this kind of thing. For unit-tests I would usually mock them. If I would want to test getItems(), I would actually try to test it through the database (e.g. via testcontainers) in an integration test, because this rather close to the database anyway.
In your concrete case I would probably not even try to test getItems(), since it only seems to contain some plumbing code.
As a rule of thumb: It is usually not a good idea to mock what you do not own.

Related

How can code be divided to structure of MVC (except view)for Using JPA(transaction)?

Wanted to divide the development logics structure.
So i divided controller, repository, reposiotryImpl, vo, service.
controller, vo, repository are fine.
But for repositoryImpl and service are difficult divide logics because of transcaction codes in service.
I am using hybernate jpa in sts4.
Blow code is used in service.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
Below code is used in Repository interface.
public interface LoginRepository {
List<UserInfo> findUser(String id, String pw);
}
Below code is usde in RepositoryImpl.
public class LoginRepositoryImpl implements LoginRepository{
UserInfo userInfo = new UserInfo();
#Override
public List<UserInfo> findUser(String id, String pw) {
userInfo.setId(id);
userInfo.setPw(pw);
// it didn't finish the code yet.
return null;
}
}
So i think emf and em objects can go RepositoryImpl class so they can use that objects for using data CRUD. But problem is tx object seems like have to remain in service class for transaction.
Or emf, em, tx all should stay in Service class?
As you said, the service class and its implementation class are generally difficult to separate, so the package structure of the service class and its implementation class is generally as follows
**.**.**.service
**.**.**.service.impl
, However, in daily development, almost all businesses are implemented in impl. For better extension and maintenance, you cannot put other classes into the impl package, even if it is beneficial to your development, if you do sub-do, your project may not have a way to be maintained by others.
This is my understanding, and the developers around me are doing the same.

How to test EntityManager query with H2 in memory DB

I have a Spring Boot project and I want to test some queries. I want to insert a predefined set of data and execute the Repository query to check the result is the desired one.
To do this I'm using in-memory H2 DB and the problem (I think) is not there, everything related with DB is ok. The main problem is I can't mock properly the EntityManager field in repository and the query is always null.
My repository is like this:
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager entityManager;
#Override
public Result runQuery() {
TypedQuery<Result> query = entityManager.createQuery(
"SELECT ...", Result.class);
return query.setParameter("...", "...") // here 'query' is always null
.setMaxResults(1)
.getResultStream()
.findFirst()
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found"));
}
}
It works nice when is executed out of tests, but trying to run this test file it throw an error:
#RunWith(SpringRunner.class)
public class MyRepositoryTest {
#Mock
EntityManager entityManager;
#InjectMocks
MyRepositoryImpl repository;
#Test
public void it_should_works() {
Result r = repository.runQuery();
assertNull(r);
}
}
The repository is mocked and is not null, I can call the method. But inside the repository, as query field is null, it throw a NullPointerException when try to execute.
I've searched over the internet and I've found many ways to test the JPARepository and #Query inside the interface, but not an EntityManager query.
Also I've found a few ways to mock the result for the query, something like when(runQuery()).thenReturn(result) but I don't want that, I've the data in the memory DB so I want to execute the query and get the result.
So, now, the main problem I think is how to mock the EntityManager object properly inside repository class.
Thanks in advance.
Edit:
I've follow this link and is like another SO questions: It's only to mock the JpaRepository.
I've used this code:
#Test
public void it_should_works() {
Result r = repository.findAll();
assertNotNull(r);
}
And works perfectly, but using my own query fails with error:
org.springframework.orm.jpa.JpaSystemException: could not advance using next(); nested exception is org.hibernate.exception.GenericJDBCException: could not advance using next()
...
Caused by: org.h2.jdbc.JdbcSQLNonTransientException: El objeto ya está cerrado
The object is already closed [90007-200]
So the question is: It is related to my DB? Why using JpaRepository method it works but my own query don't?
Edit:
Solved adding #Transactional into repository.
Since you're using an h2 in-memory database to run tests, and you want to actually use that database in your tests, you shouldn't really be mocking anything.
Your mocking doesn't work because the MyRepositoryImpl is typically initialized by Spring, and that process is much more complicated than plugging in an EntityManager.
I think what you want to do is more like what's described here https://www.baeldung.com/spring-testing-separate-data-source
So you would have a src/test/resources/application.properties file that overrides the datasource properties. Then you just #Autowired your repository into your test class like normal.

JUnit: how to test method which deletes entity (JPA)

I have simple crud repository:
public interface BookRepository extends CrudRepository<Book, Long> {
#Modifying
#Transactional
void deleteById(Long id);
}
How can I write simple JUnit test to check if this method works fine? Of course, in this case, I have entity Book
You should not.
Unit testing Spring Data repositories is a problem of Spring Data developers - as a developer you are using their API and it should working that's all. Care about your logic not the library - focus on integration tests
Anyway if you really want to do this - what I would do would be to mock EntityManager and somehow inject it to the Repository implementation (sounds like horrible task), or I would use DBUnit test, prepare test data and simply would check whether object was deleted (this would be prefered way)
Usually Spring Data repositories do not tests, but If you really want to test it you can use #DataJpaTest and inject TestEntityManager.
Example:
#DataJpaTest
class BookRepositoryTest {
#Autowired
private BookRepository repository;
#Autowired
private TestEntityManager em;
#Test
void deleteById() {
Book book = ...;
final Long id = em.persistAndGetId(book, Long.class);
repository.deleteById(id);
em.flush();
Book after = em.find(Book.class, id);
assertThat(after).isNull();
}
}
Idea is that you use TestEntityManager for all operation exclude your. In the example you are persist and find entity via TestEntityManager but delete via your custom repository.

#Transactional annotation Spring boot 2.0 and hibernate LazyInitializationException

I have the following question. From what I understand the #Transactional annotation is supposed to keep the session alive, thus enabling to lazy fetch child entities without the need to performe a specific joining query.
I have the following scenario where I do not understand why I'm still getting a LazyInitializationException.
My app runs a resolver in order to provide the various controller services with a resolved object so that it can be used directly.
Said resolver intercepts a header from the request and using it's value attempts to query the db in order to fetch the object. Now the object in question is quite simple is it's doings albeit it has a list of two sub-entities.
In order to perform the resolving action I'm using an extra service where I basically wrap some JpaRepository methods. The complete is below:
#Service
public class AppClientServiceImpl implements AppClientService {
private static final Logger LOGGER = LoggerFactory.getLogger(AppClientServiceImpl.class.getCanonicalName());
private final AppClientRepository repository;
#Autowired
public AppClientServiceImpl(AppClientRepository repository) {
this.repository = repository;
}
#Override
#Transactional(readOnly = true)
public AppClient getByAppClientId(final String appClientId) {
LOGGER.debug("Attempting to retrieve appClient with id:: {}", appClientId);
return repository.findByAppClientId(appClientId);
}
#Override
#Transactional
public void saveAndFlush(final AppClient appClient) {
LOGGER.debug("Attempting to save/update appClient:: {}", appClient);
repository.saveAndFlush(appClient);
}
}
As you can see both methods are annotated as #Transactional meaning that the should keep the session alive in the context of that said method.
Now, my main questions are the following:
1) Using the debugger I'm seeing even on that level getByAppClientId the list containing on the sub-entities which is lazy loaded has been resolved just fine.
2) On the resolver itself, where the object has been received from the delegating method, the list fails to be evaluated due to a LazyInitializationException.
3) Finally on the final controller service method which is also marked as #Transactional, the same as above occurs meaning that this eventually fails to it's job (since it's performing a get of the list that has failed to initialize.
Based on all the above, I would like to know what is the best approach in handling this. For once I do not want to use an Eager fetching type and I would also like to avoid using fetch queries. Also marking my resolver as #Transactional thus keeping the session open there as well is also out of the question.
I though that since the #Transactional would keep the session open, thus enabling the final service method to obtain the list of sub-entities. This seems not to be the case.
Based on all the above it seems that I need a way for the final service method that gets call (which needs the list on hand) to fetch it somehow.
What would the best approach to handle this? I've read quite a few posts here, but I cannot make out which is the most accepted methods as of Spring boot 2.0 and hibernate 5.
Update:
Seems that annotating the sub-entitie with the following:
#Fetch(FetchMode.SELECT)
#LazyCollection(LazyCollectionOption.TRUE)
Resolves the problem but I still don't know whether this is the best approach.
You initialize the collection by debugging. The debugger usually represents collections in a special way by using the collection methods which trigger the initialization, so that might be the reason why it seems to work fine during debugging. I suppose the resolver runs outside of the scope of the getByAppClientId? At that point the session is closed which is why you see the exception.
I created Blaze-Persistence Entity Views for exactly that use case. You essentially define DTOs for JPA entities as interfaces and apply them on a query. It supports mapping nested DTOs, collection etc., essentially everything you'd expect and on top of that, it will improve your query performance as it will generate queries fetching just the data that you actually require for the DTOs.
The entity views for your example could look like this
#EntityView(AppClient.class)
interface AppClientDto {
String getName();
}
Querying could look like this
List<AppClientDto> dtos = entityViewManager.applySetting(
EntityViewSetting.create(AppClientDto.class),
criteriaBuilderFactory.create(em, AppClient.class)
).getResultList();

Unit test passes when marked #Transactional but fails when not

Have a JUNIT test set up as such
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/applicationContext.xml", "/applicationContext-security.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class BlahIntegrationTests{
#Test
public void testMappingsOfHugeObjectGraph(){
}
}
I'm attempting to test that my hibernate mappings (annotation driven and JPA based) are correct and when run like above my test passes (just asserts that an ID is created).
If I take the #Transactional away, I get errors with some of my relationships which I was expecting. Anyone have thoughts on why it's not failing when it's #Transactional?
EDIT: To Clarify, the exception that was thrown was regarding bad hibernate mappings (it's a very large object structure and I had borked some of them) upon saving of the object
If you remove #Transactional your test DB won't be empty for another test and therefore tests won't be isolated.
If you have it there then some tests may pass even though they should fail (as you described, or another example is if you insert entity wich duplicates some unique constraint).
Solution is to have #Transactional in its place and inject
#PersistenceContext
private EntityManager em;
and do following before you extract your data from database
em.flush();
em.clear();
The first line will cause synchronization between session and database (your provider usually waits till the end of the transaction).
The second line will remove all entities from session so all queries will go to the database.
And after the test everything is still rolled back so you have your database in original state.
Hope it helps.
If you take away #Transactional the Hibernate factory is running in its equivalent of auto-commit mode where each new access you make generates an entire new Session. So once you've retrieved an object it is immediately no longer associated with an open session and ineligible for Lazy Loading.

Categories

Resources