RE: Unit testing of services?

thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Has anyone ever attempted to write a unit test for the service layer? I'm starting to get into the thick of writing the custom services needed by my custom portal, and it occurred to me that I'd REALLY like to test the service layer with some unit tests outside of the Tomcat container. Something tells me that will be nearly impossible.

Anyone try it before?
thumbnail
11121, modified 19 Years ago. Liferay Master Posts: 518 Join Date: 7/20/05 Recent Posts
Mike just added some to core (I think it was released with 4.2). Take a look at
/trunk/test/src/com/liferay/test/ServiceTests.java.
thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Thanks Alex.

I took a peek at the unit tests, and they appear to be clients that access the services via the remote interfaces (i.e. they are for things like JournalServiceUtil, and not JournalLocalServiceUtil). Without further investigation, I THINK they will still require that the portal code be running inside of a container.

I'm really looking for a way to test the LOCALService interfaces, and ideally outside of any container.

In another project I worked on (Spring based also), I had a unit test based on Spring's AbstractTransactionalDataSourceSpringContextTests, which is part of Spring's testing framework.. It was pretty cool in that it used Hypersonic's in-memory database. You'd start the test, which would load the Spring contexts, create the database (in memory), then call the services. That way, the tests could be deterministic.

I'm looking for doing the same thing with my Liferay local service implementations. I think I'm on to something (I've started playing around with it). I think I can turn a decendant class of AbstractTransactionalDataSourceSpringContextTests into a com.liferay.portal.kernel.bean.BeanLocator, specify it for com.liferay.portal.kernel.bean.BeanLocatorUtil.setBeanLocator(), and thus have a class that will allow quick testing of service code without having to fire up the container.

If I get something going, I'll report back here.
thumbnail
11121, modified 19 Years ago. Liferay Master Posts: 518 Join Date: 7/20/05 Recent Posts
Joel Kozikowski:
Without further investigation, I THINK they will still require that the portal code be running inside of a container.

Actually, no. They can run outside a container.

First, configure the /trunk/test/classes/test.properties. Then, run from /trunk/test one of the targets like "ant test-service".

I'd be interested in seeing what you come up with, though. Testing is something that can always be improved on.
thumbnail
11365, modified 19 Years ago. Liferay Master Posts: 846 Join Date: 8/5/04 Recent Posts
Hey Joel,

com.liferay.portal.service.ServiceTests specifically runs outside an appserver, ie you can run it without starting Liferay. There's only one simple test in there right now, but this is going to serve as the framework for future unit tests.

The user principal is injected at runtime and the datasources are injected using commons BasicDataSource.

Any questions let me know emoticon
thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Hey Mike -

I took a closer look at the new testing code, and I see now that it is designed to run outside of the container. I tried to copy it into my extension environement and give it a run, but I ran into a problem - no Hibernate session or transaction.

My goal is to be able to run an individual test case (over and over again) from within Eclipse as I first develop the service. I don't want to launch it from Ant (I want to be able to step through the code and debug it).

Anyway - being time constrained, I fell back on "what I know." I'm making my own version of the BaseServiceTest class based on the Spring class AbstractTransactionalSpringContextTests, but then adding in the permission stuff you had from your class. That Spring class creates a transaction for you. The other nice thing about it - it rolls back the transaction after it executes (regarless of success or failure, unless you override that behavior). So, you can run the test, and if it fails, there is no need to reset the database.

I'm working on the code now (and making good progress). I'm also adding some code for creating a mock set of data (a fake company, organization, location, and user). I'm going to try to get to the point I had in the other unit tests I did: Be able to run it with no pre-existing database whatsoever (i.e. build a Hypersonic in memory db, create the mock data, and run the test).

I'll report back here when I've got something completed.
thumbnail
11365, modified 19 Years ago. Liferay Master Posts: 846 Join Date: 8/5/04 Recent Posts
What you're working on actually sounds interesting. Are you saying that our tests are not run in a tx?
thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Michael Young:
Are you saying that our tests are not run in a tx?


Well, I'm saying that when I copied the code to my extension environment and tried to run it, as soon as any code tried to touch the database, I got an error that Hibernate had no session attached to the thread. So, no Hibernate session, yet alone a transaction.

I have to assume that if you can in fact run the tests from within Ant, then it was probably a configuration issue on my end. There was no obvious code in your BaseServiceTest.java (or its descendants) that created the session and the transaction, but then again, I didn't look very hard.

All I know is that when I switched to a derivative of Spring's AbstractTransactionalSpringContextTests, it started working.

It may have been a configuration issue on my end. Remember, my goal is to simply right click on my test case from within Eclipse and select "Run as" or "Debug as". The (test framework) code I have works, and I know its in a transaction (because the code I'm actually TESTING doesn't work, and I see it keeps rolling it back emoticon )
thumbnail
11365, modified 19 Years ago. Liferay Master Posts: 846 Join Date: 8/5/04 Recent Posts
In our code the session injected by spring. If you look at classes/portal-test.properties, it includes test-spring-professional.xml as part of the the spring configs. The session information is configured there.

Ah the beauty of easyconf...
thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Michael Young:
In our code the session injected by spring. If you look at classes/portal-test.properties, it includes test-spring-professional.xml as part of the the spring configs.


Yup - I saw that. I also have that file (test-spring-professional.xml) in my path (and referenced in my new code). It was accesible to the "old code" also.

All I see in that file is a configuration of a data source. I don't see a Hibernate session.

In any event - I've got something running.
thumbnail
18941, modified 19 Years ago. Expert Posts: 405 Join Date: 6/28/06 Recent Posts
Oh wait - sorry.

I forgot - I had to modify that file (test-spring-professional.xml). The hibernate-test.properties reference was commented out in my copy (from SVN). I could not find the hibernate properties file anywhere, so I figured that was still a work in progress.
thumbnail
11365, modified 19 Years ago. Liferay Master Posts: 846 Join Date: 8/5/04 Recent Posts
We hadn't gotten it to work with hibernate-test.properties, so we're just inlining the connection properties in test-spring-professional.xml
713751, modified 18 Years ago. New Member Posts: 19 Join Date: 4/24/08 Recent Posts
Joel Kozikowski:

Yup - I saw that. I also have that file (test-spring-professional.xml) in my path (and referenced in my new code). It was accesible to the "old code" also.

All I see in that file is a configuration of a data source. I don't see a Hibernate session.

In any event - I've got something running.


Hi Joel

I am also trying to unit-test my services and followed this thread and your descriptions (test-case implementation based on AbstractTransactionalSpringContextTests).

I got to the point that my service is called from the test-case but I keep getting the following exception from my persistence class:

12:32:02,515 ERROR [HibernateUtil:211] java.lang.IllegalArgumentException: 
No positional parameters in query: FROM com.alyportal.model.VAccount WHERE userId = ? AND accountType = ? 
  java.lang.IllegalArgumentException: No positional parameters in query: 
    FROM com.alyportal.model.VAccount WHERE userId = ? AND accountType = ? 
	at org.hibernate.impl.AbstractQueryImpl.setParameter(AbstractQueryImpl.java:332)
	at org.hibernate.impl.AbstractQueryImpl.setLong(AbstractQueryImpl.java:489)
	at com.myportal.service.persistence.VAccountPersistenceImpl.fetchByUserId_Type(VAccountPersistenceImpl.java:292)
	at com.myportal.service.persistence.VAccountPersistenceImpl.findByUserId_Type(VAccountPersistenceImpl.java:227)
	at com.myportal.service.persistence.VAccountUtil.findByUserId_Type(VAccountUtil.java:57)
	at com.myportal.service.impl.VAccountLocalServiceImpl.getAccount(VAccountLocalServiceImpl.java:50)
	at com.myportal.service.impl.VAccountLocalServiceImplTest.testGetAccount(VAccountLocalServiceImplTest.java:129)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at junit.framework.TestCase.runTest(TestCase.java:164)
	at junit.framework.TestCase.runBare(TestCase.java:130)
	at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:76)
	at junit.framework.TestResult$1.protect(TestResult.java:106)
	at junit.framework.TestResult.runProtected(TestResult.java:124)
	at junit.framework.TestResult.run(TestResult.java:109)
	at junit.framework.TestCase.run(TestCase.java:120)
	at junit.framework.TestSuite.runTest(TestSuite.java:230)
	at junit.framework.TestSuite.run(TestSuite.java:225)
	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)


I do not see why Hibernate refuses to accept the positional parameters set from the ServiceBuilder generated code. Have you encountered this problem? Can you help me out?

Thanks for your help in advance,
Richie.
713751, modified 18 Years ago. New Member Posts: 19 Join Date: 4/24/08 Recent Posts
Hi

I found the problem: I was using Eclipse to launch my unit-tests and ext/ext-impl/classes was not on the classpath. As a result, ext-hbm.xml was not found, which caused that Hibernate was unable to find the concrete implementation for my entity's interface and could not resolve the HQL query (with the positional parameters).

Sorry for spamming.

BR,
Richie.
3231740, modified 16 Years ago. New Member Posts: 2 Join Date: 6/2/09 Recent Posts
Hello Joel.
I was wondering if you managed to make your tests with the AbstractTransactionalDataSourceSpringContextTests work. If so, could you please post an example?
Thank you very much
4276145, modified 16 Years ago. New Member Posts: 2 Join Date: 11/13/09 Recent Posts
If you download the liferay source you'll see integration tests that liferay has developed to test it's own services. You can use that as a starting point for your own integration tests.
5805221, modified 15 Years ago. New Member Posts: 2 Join Date: 9/7/10 Recent Posts
Hi Joel,
Have you got any solution for unit testing of services?If yes,Please provide me any link or example.
I am using Java's JUnit Test facility. When I call any function like add<model-name> with passing arguments then the other objects needed for this function calling cannot be instantiated for example <model-name>LocalService. There is also problem in using of the static methods for example CounterServiceUtil.increment(<model-name>.class.getName()).
To solve this, I tried to know the process of object creation in the function execution.

I tried all the above with tomcat server running and without running.


Thanks in Advance
Gagan