The question has already been asked at Stackoverflow but I haven't found the answer. I can't understand how to test (unit) my DAO and Service layers (you will be able to find the methods I would like to test below). So, there are two opposite notions regarding testing of DAO: the one is not to test it at all, the opposite one is to test it only with in-memory DB. As for service layer there are meaning that method should be tested only if it has business logic. So, frankly I can't even imaging what to do because I'm not sure which the way is correct. At my last pet project I tested DAO by using jUnit only (examle for saveEntity()): I explicitly created entity, populated it, saved it by using DAO-method, when retrieved it, asserted result and explicitly deleted the object from the DB. I'm sure that it is not the proper way to test it. So, please take a look at the code below and give me an advice how to test these layers' methods properly:
DAO
#Override
public void saveEntity(Artist entity) throws ConstraintViolationException {
sessionFactory.getCurrentSession().save(entity);
}
Service
#Transactional
#Override
public void saveEntity(Artist entity) throws ConstraintViolationException {
artistDAO.saveEntity(entity);
}
I wanted to do it with Mockito but all examples I found not similar to my case.
Thank you for any ideas how to do that.
Related
My unit tests are seeing org.hibernate.LazyInitializationException: could not initialize proxy [org.openapitools.entity.MenuItem#5] - no Session. I'm not sure why they expect a session in a unit test. I'm trying to write to an in-memory h2 database for the unit tests of my Controller classes that implement the RESTful APIs. I'm not using any mock objects for the test, because I want to test the actual database transactions. This worked fine when I was using Spring-Boot version 1.x, but broke when I moved to version 2. (I'm not sure if that's what caused the tests to break, since I made lots of other changes. My point is that my code has passed these tests already.)
My Repositories extend JPARepository, so I'm using a standard Hibernate interface.
There are many answers to this question on StackOverflow, but very few describe a solution that I could use with Spring-Data.
Addendum: Here's a look at the unit test:
#Test
public void testDeleteOption() throws ResponseException {
MenuItemDto menuItemDto = createPizzaMenuItem();
ResponseEntity<CreatedResponse> responseEntity
= adminApiController.addMenuItem(menuItemDto);
final CreatedResponse body = responseEntity.getBody();
assertNotNull(body);
Integer id = body.getId();
MenuItem item = menuItemApiController.getMenuItemTestOnly(id);
// Hibernate.initialize(item); // attempted fix blows up
List<String> nameList = new LinkedList<>();
for (MenuItemOption option : item.getAllowedOptions()) { // blows up here
nameList.add(option.getName());
}
assertThat(nameList, hasItems("pepperoni", "olives", "onions"));
// ... (more code)
}
My test application.properties has these settings
spring.datasource.url=jdbc:h2:mem:pizzaChallenge;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=pizza
spring.datasource.password=pizza
spring.jpa.show-sql=true
This is not standard Hibernate, but spring data. You have to understand that Hibernate uses lazy loading to avoid loading the whole object graph from the database. If you close the session or connection to the database e.g. by ending a transaction, Hibernate can't lazy load anymore and apparently, your code tries to access state that needs lazy loading.
You can use #EntityGraph on your repository to specify that an association should be fetched or you avoid accessing the state that isn't initialized outside of a transaction. Maybe you just need to enlarge the transaction scope by putting #Transactional on the method that calls the repository and accesses the state, so that lazy loading works.
I found a way around this. I'm not sure if this is the best approach, so if anyone has any better ideas, I'd appreciate hearing from them.
Here's what I did. First of all, before reading a value from the lazy-loaded entity, I call Hibernate.initialize(item);
This throws the same exception. But now I can add a property to the test version of application.properties that says
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Now the initialize method will work.
P.S. I haven't been able to find a good reference for Spring properties like this one. If anyone knows where I can see the available properties, I'd love to hear about it. The folks at Spring don't do a very good job of documenting these properties. Even when they mention a specific property, they don't provide a link that might explain it more thoroughly.
First of All I have to apologize for the semi-psuedo code as the classes I am going to reference are all too big and are pointless except for a few lines of each class to ask my question.
To Start off I am using
Jboss 6.3.2 EAP
Hibernate
EJB 3.0
I have two different EJB Classes
They are
Service Layer
#Stateless
#TransactionManagement
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class ServiceClass implements ILocalService
{
#EJB
ILocalDao localDao;
#Override
public myObject find(String id)
{
localDao.findByPK(id);
}
#TransactionalAttribute(TransactionAttributeType.REQUIRED)
#Override
public void create(myObject obj)
{
localDao.create(obj);
}
#TransactionalAttribute(TransactionAttributeType.REQUIRED)
#Override
public void update(myObject obj)
{
localDao.findByPK(obj.getId());
localDao.update(obj);
}
}
DAO Layer
#Stateless
#TransactionalAttribute(TransactionAttributeType.MANDATORY)
public class DaoClass extends AbstractDaoClass<myObject> implements ILocalDao
{
#Override
public myObject findByPK(String id)
{
super.find(id);
}
}
Okay with those two classes laid out. Trust me that everything else is correctly configured interface Classes, Abstract Class, persistence.xml, etc.
The question is How does the find method in the ServiceClass work? With the NOT_SUPPORTED being assigned to this method I would think that the DaoClass call would throw a Exception because no Transaction has been started yet and I am making the entire class MANDATORY. But my running app says otherwise.
To verify that no transaction is being created on the DaoClass.find method. I put in the #TransactionalAttribute(TransactionAttributeType.NEVER). And re-ran the application. Still works.
To verify that the NEVER works I call the ServiceClass's update method. Throws an exception as expected.
To round this all out I have looked at many online resources(http://docs.oracle.com/javaee/6/tutorial/doc/bncij.html, etc) I have looked into the source files for the EJB 3.0 and Jboss...I am not seeing anything.
So questions I have pondered
Is the TransctionAttributeType.MANDATORY on the class level not
working as I expect it to in the DaoClass?
TransactionManagement on the ServiceClass creating a Transaction on init of the ServiceClass and then the NOT_SUPPORTED suspending this transaction?
I am in the process of writing up some tests to hopefully log the transaction's behavior and see what is going on. But hopefully someone here can shed some light sooner and relieve this huge question mark I have.
Thanks in advance.
First, #TransactionManagement without parameters is useless, since default value is TransactionManagementType.CONTAINER and EJB uses TransactionManagementType.CONTAINER implicitly.
I learnt flywaydb migration with java works with JDBC connection and also spring support through SpringTemplate, but flyway doesn't work with DAOs.
for tables/entities with more relationships,it makes life much easier to do migration with DAO's rather than sql.
is there a solution or work-around to deal with this ?
First, Flyway has its own transaction managing system and does not use Spring transaction handling.
If your DAOs extend JdbcDaoSupport, you could instantiate manually the your DAO and then manually inject the provided JdbcTemplate in the DAO:
public class MyJdbcMigration implements SpringJdbcMigration {
public void migrate(JdbcTemplate jdbcTemplate) {
MyJdbcDao dao = new MyJdbcDao();
dao.setJdbcTemplate(jdbcTemplate);
dao.updateDate();
}
}
I know this comes very late, but for future visitors with the same problem this might be helpful.
In my opinion, the creator of Flyway is actually wrong in this subject. It's perfectly fine to migrate data with business logic and there is no chicken and egg problem, as long as you do not change the structure of the database in your update script.
One example: you have a field "password" in your database and it is clear text. Because of security concerns you now want to use a special hash function and hash all passwords in the database (it should be a secure one and the database does not have a function to do that). The hash function is declared in your UserDAO and called when the user is created or when they change their password. Although that's not a perfect example, there are many possible scenarios where accessing a DAO for the migration makes sense.
Fortunately a work colleague of mine found a solution to the problem, and it only requires around 5 lines of code. You also need to add Apache Deltaspike to your dependencies, if it isn't already.
In your DAO, add an import for BeanProvider:
import org.apache.deltaspike.core.api.provider.BeanProvider;
Then we simply make the DAO a singleton:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
That's pretty much it. In your Flyway script you can now access the DAO:
#Override
public void migrate(Connection cnctn) throws Exception{
UserDao userdao = UserDao.getInstance();
List<User> userList = userdao.getAllUsers();
...
}
Explanation: the Class (VX_yourflywaymigrationscript) is not managed by the CDI Container, so it's not possible to inject the DAO. BeanProvider is made for exactly that - it can load a Bean and give you the reference, even if you are not in a CDI context.
I hope that helps.
Your DAOs rely on the very structure Flyway was designed to change. We therefore have a chicken and egg problem here. The way to solve this is to run Flyway before the rest of your application (including the DAOs) gets initialized.
I have written some code which I thought was quite well-designed, but then I started writing unit tests for it and stopped being so sure.
It turned out that in order to write some reasonable unit tests, I need to change some of my variables access modifiers from private to default, i.e. expose them (only within a package, but still...).
Here is some rough overview of my code in question. There is supposed to be some sort of address validation framework, that enables address validation by different means, e.g. validate them by some external webservice or by data in DB, or by any other source. So I have a notion of Module, which is just this: a separate way to validate addresses. I have an interface:
interface Module {
public void init(InitParams params);
public ValidationResponse validate(Address address);
}
There is some sort of factory, that based on a request or session state chooses a proper module:
class ModuleFactory {
Module selectModule(HttpRequest request) {
Module module = chooseModule(request);// analyze request and choose a module
module.init(createInitParams(request)); // init module
return module;
}
}
And then, I have written a Module that uses some external webservice for validation, and implemented it like that:
WebServiceModule {
private WebServiceFacade webservice;
public void init(InitParams params) {
webservice = new WebServiceFacade(createParamsForFacade(params));
}
public ValidationResponse validate(Address address) {
WebService wsResponse = webservice.validate(address);
ValidationResponse reponse = proccessWsResponse(wsResponse);
return response;
}
}
So basically I have this WebServiceFacade which is a wrapper over external web service, and my module calls this facade, processes its response and returns some framework-standard response.
I want to test if WebServiceModule processes reponses from external web service correctly. Obviously, I can't call real web service in unit tests, so I'm mocking it. But then again, in order for the module to use my mocked web service, the field webservice must be accessible from the outside. It breaks my design and I wonder if there is anything I could do about it. Obviously, the facade cannot be passed in init parameters, because ModuleFactory does not and should not know that it is needed.
I have read that dependency injection might be the answer to such problems, but I can't see how? I have not used any DI frameworks before, like Guice, so I don't know if it could be easily used in this situation. But maybe it could?
Or maybe I should just change my design?
Or screw it and make this unfortunate field package private (but leaving a sad comment like // default visibility to allow testing (oh well...) doesn't feel right)?
Bah! While I was writing this, it occurred to me, that I could create a WebServiceProcessor which takes a WebServiceFacade as a constructor argument and then test just the WebServiceProcessor. This would be one of the solutions to my problem. What do you think about it? I have one problem with that, because then my WebServiceModule would be sort of useless, just delegating all its work to another components, I would say: one layer of abstraction too far.
Yes, your design is wrong. You should do dependency injection instead of new ... inside your class (which is also called "hardcoded dependency"). Inability to easily write a test is a perfect indicator of a wrong design (read about "Listen to your tests" paradigm in Growing Object-Oriented Software Guided by Tests).
BTW, using reflection or dependency breaking framework like PowerMock is a very bad practice in this case and should be your last resort.
I agree with what yegor256 said and would like to suggest that the reason why you ended up in this situation is that you have assigned multiple responsibilities to your modules: creation and validation. This goes against the Single responsibility principle and effectively limits your ability to test creation separately from validation.
Consider constraining the responsibility of your "modules" to creation alone. When they only have this responsibility, the naming can be improved as well:
interface ValidatorFactory {
public Validator createValidator(InitParams params);
}
The validation interface becomes separate:
interface Validator {
public ValidationResponse validate(Address address);
}
You can then start by implementing the factory:
class WebServiceValidatorFactory implements ValidatorFactory {
public Validator createValidator(InitParams params) {
return new WebServiceValidator(new ProdWebServiceFacade(createParamsForFacade(params)));
}
}
This factory code becomes hard to unit-test, since it is explicitly referencing prod code, so keep this impl very concise. Put any logic (like createParamsForFacade) on the side, so that you can test it separately.
The web service validator itself only gets the responsibility of validation, and takes in the façade as a dependency, following the Inversion of Control (IoC) principle:
class WebServiceValidator implements Validator {
private final WebServiceFacade facade;
public WebServiceValidator(WebServiceFacade facade) {
this.facade = facade;
}
public ValidationResponse validate(Address address) {
WebService wsResponse = webservice.validate(address);
ValidationResponse reponse = proccessWsResponse(wsResponse);
return response;
}
}
Since WebServiceValidator is not controlling the creation of its dependencies anymore, testing becomes a breeze:
#Test
public void aTest() {
WebServiceValidator validator = new WebServiceValidator(new MockWebServiceFacade());
...
}
This way you have effectively inverted the control of the creation of the dependencies: Inversion of Control (IoC)!
Oh, and by the way, write your tests first. This way you will naturally gravitate towards a testable solution, which is usually also the best design. I think that this is due to the fact that testing requires modularity, and modularity is coincidentally the hallmark of good design.
So I have a interesting problem that i will need some help with. I know a bunch of questions have been asked around rollbacks in transactions using junit but I believe my problem and slightly different. To give people a better understanding of the problem let me start from the beginning.
I have implemented a UserManagementService with its respective DAO for a user management system. There is a general method called CreateUser(User obj) that is used to create a unique user. Now, there is a constraint set that email addresses are unique so if we try to invoke this method with a email address that has already been used, we throw a custom exception called UserManagementException with its respective error message. All this works fine however, the problem I am having is when it comes to the unit test. Oh, before i forget, let me mention the software stack i am using [Java, spring, hibernate]
I have my unit test class annotated with the Transactional annotations for each method that actually hits the db. These methods also have the #Rollback annotation so that all inserts, updates and deletions are rolled back at the end of each test invocation. So the problem i am facing here is I would like to test for the unique user constraint scenario. By calling the createUser(obj) a second time with a user object with the same email address I want to ensure that the UserManagementException exception is thrown. However, since it is transactional, whenever a exception is thrown, the transaction is rollback before the unit test completes and hence fails the test. Below is the test case.
#Test
#Rollback
#Transactional
public void testUniqueCreateConsoleUser() {
boolean success;
ConsoleUser newUser;
//first one
userManagementDao.createConsoleUser(user);
//second one. This shd throw a UserManagementException
try {
//now try and insert a new user with same email
newUser = new ConsoleUser("Queen", "Kong", "king.kong#blah.com", "kingkong","Universal Studios", "America/Los_Angeles", false, null);
userManagementDao.createConsoleUser(newUser);
//if this passed this is a problem. Console users should have unique email address
success = false;
} catch (UserManagementException e) {
success = true;
}
Assert.assertTrue(success);
}
The weird thing is when i am running it through the debugger, the Assert.assertTrue() method is invoked correctly but the test ultimately fails.
Another thing i tried was to add a prop to the #Transactional annotation. I added the flowing #Transactional(noRollbackFor = UserManagementException.class) in hopes that if the exception was thrown, the rollback wouldn't be invoked then but at the end of the test. I may be approaching this the wrong way so any ideas or best practices around this sort of testing would be greatly appricieated.
Note: Below is a snippet from the stacktrace..
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:695)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:321)
at org.springframework.transaction.aspectj.AbstractTransactionAspect.ajc$afterReturning$org_springframework_transaction_aspectj_AbstractTransactionAspect
It's hard to tell from your example, but you seem to be testing against your actual DAO implementation. Rather than have unit test data hitting your actual database, mock your DAO with either a mock implementation or a mocking framework. You can then manipulate the data returned programmatically and contort it into whatever validation scenarios you want.
If you can confirm that an extra rollback is thrown (for example - when spring does the insert, when it sees that it fails, does it already roll the transaction back?) then you should catch the rollback, or configure spring not to roll the transaction back.
That is, clearly, the rollback which spring is implementing is conflicting with the expected rollback in your unit test. This rollback is then confusing the rollback annotation, causing an unexpected thrown exception in the "unit-test / Spring ether".
THE SIMPLE SOLUTION : Don't enable the automated rollbacks for this test. Tests don't always have to be perfectly elegant.
Rather than inserting a user and then inserting another user with the same email address I suggest first loading an existing user from the database and then attempting to insert anther with the same email address as the one that was retrieved. If so you simply need to do:
#Test(expected = UserManagementException.class)
public void insert_duplicate_user() throws Exception {
// Read user from database
final ConsoleUser user = dao.load(...);
// Create new user with same email address.
final ConsoleUser newUser = new ConsoleUser (...);
newUser.setEmail(user.getEmail());
// Write
dao.createConsoleUser(newUser);
/*
* If you get here, there is a problem with your DAO logic
* and a new user (with the same email was created).
* So, we need to clean that up
*/
// Delete new user
dao.deleteUser(newUser);
}
This test will fail unless a UserManagementException is thrown.