Questions regarding best use of testng and mockito - spring

I am very new for testng(unit testing) and mockito. I have read some articles and went through some code snippets on Internet. But still I have some doubts regarding unit testing with testng & mockito in spring framework.
For unit testing a service layer we mock a DAO. What if I want to test a function wich fetch some data from database and do some operations. How does mock DAO works here. From where mocked DAO will get some data for testing such a function.
If am doing a validation like Data not present in database and I want to test wheather it throws correct exception for that. So it needs some values in database and mocked DAO will check if data present in that predefined database(in-memory). How to provide such a data.
Does dataprovider helps to provide a data to used by DAO. If yes, How it can be done?
Please correct me if my understanding regarding unit testing is correct. Please let me know where I am getting it wrong if I miss understood a concept.
Thank you.

1) Besides UnitTests, you also need Integration and / or Acceptance-Tests.
The Unit-Tests will test that your SUT - Single Unit of Test, in this case a specific service class works as intended, without integrating it with other classes or systems (DB). However, additionally I would write an Integration Test for this Service that retrieves / manipulates test data from the database. This test should ideally not make any assumptions about the data in the database, so inserting the data you will be looking for, before executing the test, is recommended, e.g. using a #Before annotation and actually committing this test data into the test database. However, I further recommend you to do a proper cleanup of the database, in the #After test method. Auto rolling back the data could be done, but is not as optimal, especially if you have a persistence framework like Hibernate or JPA in between. Only when you work on committed data that is really in the physical (not virtual!) dabase, you can be 100% sure your test succeeded.
If I correctly understood your intend, this actually sounds like a perfect reason for mocking your DB / persistence object - make it throw the expected exception / return an empty result, that test that your code behaves as expected on this condition.
A TestNG Dataprovider actually does the opposite of what you are looking for - it is a way to provide an array of data to your test method:
org.testng.annotations.DataProvider
Annotation Type DataProvider
Mark a method as supplying data for a test method. The data provider name defaults to method name. The annotated method must return an Object[][] where each Object[] can be assigned the parameter list of the test method. The #Test method that wants to receive data from this DataProvider needs to use a dataProvider name equals to the name of this annotation.

Related

Is it a good practice to Mock entity manager in spring boot unit testing

I currently design an API using spring boot. In my service layer, I use Entity Manager for accessing the database. I have provided a method in my service layer below as an example.
public Object getFirstReturnDate(Long vehicle_id, LocalDateTime reservation_date){
String string = "SELECT r.reservation_time from Reservation r left join fetch r.driverAssignment where r.reservation_time > :reservation_time " +
"and r.reserved_status.slug in ('pending','approved') and r.reserved_vehicle.id=:vehicle_id " +
" order by r.reservation_time asc ";
Query query = em.createQuery(string);
query.setParameter("reservation_time",reservation_date);
query.setParameter("vehicle_id",vehicle_id);
List<LocalDateTime> localDateTimes=query.getResultList();
if(localDateTimes.size()==0)
return new DefaultResponseDTO(200, ResponseStatus.OK,"Any Date",null);;
return new DefaultResponseDTO(200, ResponseStatus.OK,"Possible Time",localDateTimes.get(0));
}
in the testing unit, I mocked the entity manager as below,
#Mock
EntityManager em;
And in the test method,
Query query = Mockito.mock(Query.class);
Mockito.when(em.createQuery(Mockito.anyString())).thenReturn(query);
Mockito.when(query.getResultList()).thenReturn(new LinkedList());
My question is, if I mock entity Manger as I mentioned above then the query in the method didn't get checked. Is it a bad coding practice?
Is there any other way to check queries without calling the database?
Any testing approach should be part of a comprehensive testing strategy. I.e. you should know which types of bugs you expect your unit tests to find, which types of bugs you expect integration and E2E tests to find, etc.
Mocking an EntityManager has pros:
It is easier to test your business logic in isolation
You can set up hypothetical scenarios with ease
Your tests will be much faster as you don't establish a connection to the database
But also cons:
You do not test your database connection and configuration
So the question is, where are you testing for database integration issues if they are not check in this test? Once you know the answer to that question you will know the answer to the one you have asked above.
I would recommend reviewing the "Testing Strategies in a Microservice Architecture" for a more in-depth view of how to approach your testing.

Unit test class annotated by #Transactional and implications to detach/evict in hibernate

I'm struggling with a problem I and can not find out proper solution or even a cause neither in hibernate docs, sources nor S/O.
I have spring/hibernate application with DAO-Service-RPC layers, where DAO provides Hibernate entities and Service DTOs for RPC. Therefore I'm converting (mapping by Dozer) Entities to DTOs in service methods and mapping DTOs back to Entities there as well.
Mapping is as follows (not full method, checking ommited):
#Transactional
public updateAuthor(Author author) {
AuthorEntity existingEntity = this.authorDao.findById(author.getId());
this.authorDao.detach(existingEntity);
this.authorAssembler.toEntity(author, existingEntity, null);
this.authorDao.merge(existingEntity);
}
I have unit test classes annotated by #Transactional to avoid test data bleeding. Now, I realized, that there is something, I don't understand, going on in Service.
When I have my test class annotated by #Transactional, calling detach() seems to work (i.e. Hibernate is not reporting org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session, but #Version number on entity is not incremented properly (as if the parent (unit test)) TX was still holding on.
When I remove my test class annotation, mapping throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection - which makes sense on it's own, because the entity is detached from session. What I don't understand is, why this exception is NOT thrown when the test class is annotated. As I understand, entity is detached from the current session in both cases.
My other question is - assuming the entity is behaving correctly - how to avoid missing such errors in unit tests and avoid test bleeding as well, as it seems to be, this type of error manifests only with unannotated test class.
Thank you!
(JUnit4, Spring 4.0.2.RELEASE, Hibernate 4.3.1.Final)

Spring Database Integration Test, when to flush or?

I am fairly new to spring, and doing some integration tests.
Using Hibernate, MySql and Spring data JPA.
I am using transaction support and everything gets rolled back at the end of each test.
For example:
#Test (expected=DataIntegrityViolationException.class)
public void findAndDelete() {
UUID uuid = UUID.fromString(TESTID);
User user= iUserService.findOne(uuid);
iUserService.delete(cashBox);
iUserService.flush();
assertNull(iUserService.findOne(uuid));
}
In the above code, I call the iUserService.flush(), so that the sql gets sent to the DB, and an expected DataIntegrityViolationException occurs because there is a foreign key from User to another table (Cascade is not allowed, None). All good so far.
Now, if I remove the iUserService.flush()
then the expected exception does not occur because the sql does not get sent to the DB.
I tried adding the flush() into a teardown #After method, but that didn't work as the test does not see the exception outside of the test method.
Is there any way to avoid calling the flush within the test methods?
It would be preferable if the developers on my team did not have to use the flush method at all in their testing code
Edit:
I tried adding the following
#Before
public void before() {
Session session = entityManagerFactory.createEntityManager().unwrap(Session.class);
session.setFlushMode(FlushMode.ALWAYS);
}
but it does seem to flush the sqls, before each query.
In my humble opinion, it's better than the developers of your team know what they are doing.
It includes the things that are configured by default and the consequences of that.
Please, take a look to why you need avoid false positives when testing ORM code

Commit/Flush transactions during unit test?

I am using Spring and JUnit to write some integration tests for my DAO. I set up my test data in the beginning of the test method, and then test my DAO methods later in the same test method. The problem is that if I don't flush/commit the transaction, the EntityManager returns the same instance of the entities that I have just created in my data setup - rendering my test useless as they will always pass.
E.g.
#Test
#Transactional()
public void loadTreeBasicCase() {
// creates and saved node to DB
Node n = createNode();
// test DAO
Node result = dao.lookup(n.getId());
// verify
assertThat(n, equalTo(result));
}
One way is to expose commit() and/or flush() methods in my DAO. But I would prefer not to do that because in production code, this almost never needs to happen (let EntityManager do it's thing). Is there a way to configure this via annotations or in Spring config? I am using Spring, JPA2 with Hibernate.
You can set the defaultRollback attribute on #Transactional to reset things between tests. This doesn't sound like what you are asking for, just throwing it out there first.
Within the test, the entity manager is behaving correctly. You want to inject different behavior for testing to "disconnect" the setup from the rest of the test. One thing I did in some tests was to call flush on the entity manager directly from the test. I only had to do it a few times, but it was valuable in those cases. I did it in the test (not the DAO) so as to not provide a method on the DAO that I don't want people calling.

How to handle externally stored default values in Domain Class

I want to be able to set default values for some fields in my domain classes.
Till now I had a class which stored a Map of settings for my whole project, with a task in mind to move this map into a redis database.
The day has come and I moved all the data to redis and created a nice spring bean to get/set the values.
However...
it seems that default values are set on the domain class instance before bean is injected.
This kind of breaks the whole process.
Also... there's an issue with unit tests.
I've created a class which implements the same interface as the spring bean and holds test values. I wanted to inject it into domain classes, but this fails as well.
So right now I'm trying to find a good way to handle externally stored defauls values for my domain classes with ability to run unit tests.
Any thoughts?
There are a few different approaches you could take:
Introduce a separate bean with the default values so that those are supplied in the same way as they were before. In a separate higher level context or later on in application startup, you could then override the bean definition with the one that pulls from the database
Use a BeanPostProcessor or BeanFactoryPostProcessor to specify the default values, then use your new bean for retrieving new values
If neither of these answers is helpful, please post your setup and example code so I can get a clearer picture of what you're trying to do.
What I did in the end:
I've created a class which is connecting to Redis and gets me all the data I require.
For unit testing I've created a copy of this class, it implements the same interface but instead of getting the data from Redis it has a simple Map inside and get's the data from there. In the end it acts the same, but the data is stored internally. So in my unit tests I just inject this Unit test version of this class where appropriate.
Probably not the best solution there is but it worked for me for the last few months.

Resources