How can I test HQL query with JUnit tests?
I have mapped Entity Class:
#Entity
#Table(name = "DOE")
public DomainObjectEntity {
//some attributes
}
which represents domain objects:
public class DomainObject {
//some attributes
}
I also have repository interface for my domain objects:
public interface DomainObjectRepository {
/**
* Gets entity by some attribute
*
*/
DomainObject getDomainObjectByObjectId(String objectId);
}
this interface is implemented in
#Repository
public class DomainObjectRepositoryImpl implements DomainObjectRepository {
#Inject
private DomainObjectEntityJPARepository entityRepository;
#Override
public DomainObjectgetDomainObjectById(String parcelId) {
//this converts my entity to domain object
return entityRepository.findByDomainObjectId(DomainObjectId.getStr()).getDomainObject();
}
}
my JPA Entity repository looks like this:
public interface DomainObjectEntityJPARepository extends JpaRepository<DomainObjectEntity, String> {
/**
* get DomainObject with requested id
*
* #param objectId - objects id
* #return DomainObject
*/
#Query("some query to be tested")
DomainObjectEntity findByDomainObjectId(#Param("objectId") String objectId);
}
and finally i want to write a simple JUnit test to check if query to findByDomainObjectId returns the correct object. I think my test should look like this:
create object to put into DB
retrieve object from DB
compare objects
How can I write such test?
Any good examples of testing HQL query with junit and repositories?
if you want to test, if your method returns the correct object, just mock your entitymanager or whatever you use for querying your DB using mockito.
If you want to test if your query works, you should setup a test DB (a in memory DB is used in this case most of the time) with the same schema and simple test data already in the DB. In your test method you should only call the method with a known id and check if you get the correct object. H2 is simple to use for in-memory testing and you can use your create.sql script (if you have one) and an insert.sql script, where you define your known test data.
It is an important aspect of unit testing to only test one part of your program at a time. if you would insert into the DB in your test you would also test the insert aspect and not only the read aspect.
You just have to use a test db, like H2 which is a in memory db, and then use Junit to see the returned entity.
Related
Here is the code:
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}
JpaRepository from Spring Data JPA project.
Here is the testing code:
public class JpaAccountRepositoryTest extends JpaRepositoryTest {
#Inject
private AccountRepository accountRepository;
#Inject
private Account account;
#Test
#Transactional
public void createAccount() {
Account returnedAccount = accountRepository.save(account);
System.out.printf("account ID is %d and for returned account ID is %d\n", account.getId(), returnedAccount.getId());
}
}
Here is the result:
account ID is 0 and for returned account ID is 1
Here is from CrudReporsitory.save() javadoc:
Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.
Here is the actual code for SimpleJpaRepository from Spring Data JPA:
#Transactional
public T save(T entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
So, the question is why do we need to use the returned instance instead of the original one? (yes, we must do it, otherwise we continue to work with detached instance, but why)
The original EntityManager.persist() method returns void, so our instance is attached to the persistence context. Does some proxy magic happens while passing account to save to repository? Is it the architecture limitation of Spring Data JPA project?
The save(…) method of the CrudRepository interface is supposed to abstract simply storing an entity no matter what state it is in. Thus it must not expose the actual store specific implementation, even if (as in the JPA) case the store differentiates between new entities to be stored and existing ones to be updated. That's why the method is actually called save(…) not create(…) or update(…). We return a result from that method to actually allow the store implementation to return a completely different instance as JPA potentially does when merge(…) gets invoked.
Also, persistence implementations actually capable of dealing with immutable objects (i.e. not JPA) might have to return a fresh instance if the actual implementation requires populating an identifier or the like. I.e. it's generally wrong to assume that the implementation would just consume the entity state.
So generally it's more of an API decision to be lenient (permissible, tolerant) regarding the actual implementation and thus implementing the method for JPA as we do. There's no additional proxy massaging done to the entities passed.
You missed the second part: if the entity isn't new, merge is called. merge copies the state of its argument into the attached entity with the same ID, and returns the attached entity. If the entity isn't new, and you don't use the returned entity, you'll make modifications to a detached entity.
I'm learning tests and I consider situation when i have class like that:
#Service
#RequiredArgsConstructor
#Transactional(readOnly = true)
public class SearchCustomerService {
private final CustomerRepository CustomerRepository;
public Page<CustomerDTO> search(CustomerCriteria criteria, Pageable pageable) {
CustomerSpecification specification = new CustomerSpecification(criteria);
return customerRepository.findCustomers(specification, pageable);
}
}
How tests should looks like? I think unit tests are not that really good because what am I actually testing? I'm just getting some data from database and if I mock repository.. then I test Mockito, right?
You can create integration test that includes persistence layer (eg inmemory H2), service and meaningfull dependencies, where your input would be a proper set of Customer entities in the repository, and assert outcome of invoking search based on some criterias.
To make it a unit test, I can you could only assert the shape of CustomerSpecification but that will not guarantee that specs itself are valid for given case - thus makes small sense.
This case is similar to testing native queries execution. You don't assert SQL string, but assert result having test data set.
I'm trying to create a simple login based on the Zentask sample, however I'm getting a runtime exception in smgts2\app\controllers\Application.java at line 43.
public static Result authenticate() {
Form<Login> loginForm = form(Login.class).bindFromRequest(); //Line 43
if(loginForm.hasErrors()) {
return badRequest(login.render(loginForm));
} else {
session("user_name", loginForm.get().user_name);
return redirect(
I've uploaded the files in github: https://github.com/gscruz/smgts2-start
Looking at your project on GitHub, I think the main issues are with the JPA annotations on your Accounts model class that models your user_account database table. The mappings on your model class get exercised when you bind the form data to a Login object, since Login.validate queries user_account.
Since the name of your class does not match the name of the table, you'll need a JPA #Table annotation to explicitly state the mapping:
#Entity
#Table(name = "user_account")
public class Accounts extends Model
You'll also need #Column annotations for the fields whose names don't match up with their corresponding columns. Give that a go and see if it gets you any further.
I have successfully created numerous database-related unit tests for a Spring 2.5.6/Hibernate 3.5.3 project using AbstractTransactionalJUnit4SpringContextTests. One example, where a Device object is created and then verified that the same object is included in the list of all Device objects:
#ContextConfiguration(locations = { "classpath:/UnitTests-context.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
public class DeviceDaoTests extends AbstractTransactionalJUnit4SpringContextTests {
#Test
public void testGetDeviceList() {
Device device = new Device();
this.sessionFactory.getCurrentSession().merge(device);
Query myQuery = this.sessionFactory.getCurrentSession().createQuery("from Device");
List<Device> allDevices = myQuery.list();
assertTrue(allDevices.contains(device);
}
}
This test works.
The Device class, which is mapped to a table named DEVICES:
#Entity
#Table(name = "DEVICES")
public class Device {
//properties omitted for brevity and readability
}
There is also a class named DeviceListItem, which is mapped to a database view named DEVICESLIST_VIEW containing parts of the DEVICES table (as well as other tables):
#Entity
#Table(name = "DEVICESLIST_VIEW")
public class DeviceListItem {
//properties omitted for brevity and readability
}
Now if I want to test that inserting a Device in the database results in a DeviceListItem with the same id being found from the list of all DeviceListItem objects (similarily to the first test), there is a problem - the following test fails:
#Test
public void testGetDeviceListItemList() {
Device device = new Device();
this.sessionFactory.getCurrentSession().merge(device);
Query myQuery = this.sessionFactory.getCurrentSession().createQuery("from DeviceListItem");
List<DeviceListItem> allDeviceListItems = myQuery.list();
assertTrue(allDeviceListItems.get(0).getId().equals(device.getId())
}
All the mappings, DAO classes etc. work when running the application on the web server - only the aforementioned unit test fails. This is probably because a new object of type Device is saved, but the items read are of type DeviceListItem and Hibernate does not "know" that they both refer to the same underlying database table and becase the objects are not really saved to the database.
Is there a way to make this test work, i.e. to test the scenario where a Device object is written to the database and read from the database as a DeviceListItem object?
(Please note that there might by typos, style issues etc. in the code examples, as the examples are strongly simplified and are not necessarily copy/pasted from actually executed code)
Solved by adding annotation
#Rollback(false)
(see http://static.springsource.org/spring/docs/2.5.6/reference/testing.html) to the method
testGetDeviceListItemList
(see original question). However, this means also that the Spring Unit test context does not make an automatic rollback, so I have to ensure that the database state is returned to normal afterwards manually:
this.sessionFactory.getCurrentSession().delete(device);
Complete method:
#Rollback(false)
#Test
public void testGetDeviceListItemList() {
Device device = new Device();
this.sessionFactory.getCurrentSession().merge(device);
Query myQuery = this.sessionFactory.getCurrentSession().createQuery("from DeviceListItem");
List<DeviceListItem> allDeviceListItems = myQuery.list();
assertTrue(allDeviceListItems.get(0).getId().equals(device.getId());
this.sessionFactory.getCurrentSession().delete(device);
}
Here is the code:
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {}
JpaRepository from Spring Data JPA project.
Here is the testing code:
public class JpaAccountRepositoryTest extends JpaRepositoryTest {
#Inject
private AccountRepository accountRepository;
#Inject
private Account account;
#Test
#Transactional
public void createAccount() {
Account returnedAccount = accountRepository.save(account);
System.out.printf("account ID is %d and for returned account ID is %d\n", account.getId(), returnedAccount.getId());
}
}
Here is the result:
account ID is 0 and for returned account ID is 1
Here is from CrudReporsitory.save() javadoc:
Saves a given entity. Use the returned instance for further operations as the save operation might have changed the entity instance completely.
Here is the actual code for SimpleJpaRepository from Spring Data JPA:
#Transactional
public T save(T entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
So, the question is why do we need to use the returned instance instead of the original one? (yes, we must do it, otherwise we continue to work with detached instance, but why)
The original EntityManager.persist() method returns void, so our instance is attached to the persistence context. Does some proxy magic happens while passing account to save to repository? Is it the architecture limitation of Spring Data JPA project?
The save(…) method of the CrudRepository interface is supposed to abstract simply storing an entity no matter what state it is in. Thus it must not expose the actual store specific implementation, even if (as in the JPA) case the store differentiates between new entities to be stored and existing ones to be updated. That's why the method is actually called save(…) not create(…) or update(…). We return a result from that method to actually allow the store implementation to return a completely different instance as JPA potentially does when merge(…) gets invoked.
Also, persistence implementations actually capable of dealing with immutable objects (i.e. not JPA) might have to return a fresh instance if the actual implementation requires populating an identifier or the like. I.e. it's generally wrong to assume that the implementation would just consume the entity state.
So generally it's more of an API decision to be lenient (permissible, tolerant) regarding the actual implementation and thus implementing the method for JPA as we do. There's no additional proxy massaging done to the entities passed.
You missed the second part: if the entity isn't new, merge is called. merge copies the state of its argument into the attached entity with the same ID, and returns the attached entity. If the entity isn't new, and you don't use the returned entity, you'll make modifications to a detached entity.