Does anyone know how to run unit tests for Liferay portlets? I have found a lot of posts about it (e.g. http://agile-reflections.opnworks.com/2010/06/portlet-unit-testing-with-liferay-6.html) but none works nonetheless.
-
Do you want to unit test your own portlets or Liferay's built in portlets? – blank Jan 20 '11 at 19:57
-
I want to unit test my own portlets, using services created by me through Service Builder. – brandizzi Feb 01 '11 at 11:58
3 Answers
This may be overkill, but if you're looking for an Enterprise approach with continuous integration testing, this blog gives a very good example: Continuous integration on Liferay: running your Selenium 2 tests on the Tomcat 6 bundle

- 31
- 2
You need to have some third party libraries on classpath.
THe key point is having even portal-impl.jar and other portal dependencies on classpath and having InitUtil.initWithSpring(boolean);
load up core spring xml configs that you specify in spring-ext.properties in spring.congigs property, only those services you need. You may need no portal services and only the portlet ones, but this is a problem because your portlet services generated by service builder use the portal services.
Using service builder just needs good knowledge of spring and classloading.
But you need to understand the infrastructure before doing that. There are quite a lot of hacks needed... Like
BeanLocator beanLocator = new BeanLocatorImpl(PortalClassLoaderUtil.getClassLoader(), ac);
PortletBeanLocatorUtil.setBeanLocator("portlet", beanLocator);
-
I am trying to understand this code yet :) I will comment it after getting something working. – brandizzi Aug 08 '11 at 02:07
Unit testing Liferay portlets is quite complicated when ServiceBuilder is utilized.
The reason is that it generates quite heavy services that contain references not only to beans within Portlet, but even to the Portal beans generated by ServiceBuilder.
There are tools like InitUtil.init(); that lets you at least instantiate and use ServiceBuilder entities... not EntityServices though. For that you'd have to use SpringUtil.loadContext(); that requires
System.setProperty("external-properties", "testing.properties");
where testing.properties contains :
spring.configs=META-INF/ext-spring.xml,\
META-INF/base-spring.xml,\
META-INF/dynamic-data-source-spring.xml,\
META-INF/infrastructure-spring.xml,\
META-INF/shard-data-source-spring.xml,\
META-INF/hibernate-spring.xml,\
META-INF/portlet-spring.xml
These are spring definitions to be loaded for testing application context. It all would be OK, but beans from portlet-spring.xml are those heavy services containing references to Portal bean definitions like ResourceService, UserLocalService, CounterLocalService and you would have to load even META-INF/portal-spring.xml
and trust me, it's not that easy cause then you'd have to load quite a lot of other stuff.
THE ANSWER:
The truth is, that you most likely won't have to unit test portlet SB services, never. They represent entities with persistence and service layer around. Something that is not to be tested. You just have to mock them and stub their methods, right ?
And the best way for junit and integration testing as to mocking is not using *LocalServiceUtil static classes in your application, because it is almost unmockable.
You just need to create a Spring FactoryBean :
public class PortalFactoryBean implements FactoryBean {
private Class type;
public void setType(final Class type) {
this.type = type;
}
@Override
public Object getObject() throws Exception {
return PortalBeanLocatorUtil.locate(type.getName());
}
@Override
public Class getObjectType() {
return type;
}
}
public class PortletFactoryBean implements FactoryBean {
private Class type;
public void setType(final Class type) {
this.type = type;
}
@Override
public Object getObject() throws Exception {
return PortletBeanLocatorUtil.locate(type.getName());
}
@Override
public Class getObjectType() {
return type;
}
}
<bean id="somePortalBean" class="example.spring.PortalFactoryBean" lazy-init="true">
<property name="type" value="com.liferay.some.util.SomeService"/>
</bean>
<bean id="somePortletBean" class="example.spring.PortletFactoryBean" lazy-init="true">
<property name="type" value="com.example.SomeService"/>
</bean>
@Autowired
private SomeService somePortalBean;
Writing unit/integration tests for this portlet would be quite easy, right ? You just create a spring context for testing and you mock these services :
Using Service Builder is worth it, but you must have some Spring knowledge and play with it for some time. Then it spares a lot of time because it is easy to maintain.
-
This is an elegant solution but... Is really mocking such an universal solution? I do not see how I could use it for testing [custom queries](http://www.liferay.com/pt/community/wiki/-/wiki/Main/Custom+queries+in+Liferay+5.2) or some method using dynamic query in a service, for example. These methods are frequent, at least in my code, certainly should be tested and are not quite easy to be mocked, right? Nonetheless, your answer is good and useful for various situations right now anyway. – brandizzi Aug 08 '11 at 02:08
-
You're right, well last time I built portal-impl/test and put it on test classpath and I called ServiceTestUtil.initPermissions() and ServiceTestUtil.initServices() with portal.properties that lists all META-INF/spring-definitions.xml and I loaded them all + my ext-spring where I put beans from my portlet context. This will work because there is definitely every bean loaded, but it's extremely slow. More over you gotta rename META-INF in your portlet because if you have it on your classpath too, it would "cover" the Portal one. It's a lot of work man – lisak Aug 08 '11 at 07:16
-
Exactly... Next project I'm gonna avoid using ServiceBuilder to see the resulting advantages, if it is worth or not... – lisak Aug 08 '11 at 11:36