Liferay: Líder no Quadrante Mágico da Gartner pelo sexto ano consecutivo! Leia mais aqui

Liferay Testing Infrastructure

Intro #

This article summarizes some of the most relevant parts of the testing infrastructure that is used to test Liferay. This infrastructure provides a common framework which allow us to write new tests in the easiest way possible. Our main goal with all this stuff is to increase our code quality and make the portal as robust as possible

This article will describe the different kind of tests we have at Liferay, how they work and how new tests can be written. We will be talking about many different things:

  • Unit tests:
    • Goals of unit testing
    • Mocking libraries: Mockito + Powermock
  • Integration tests
    • Goals of integration testing
    • Testing out of the container
    • Liferay testing listeners support
    • Making our tests transactional
  • How can I run the tests?

Liferay QA also uses Selenium to write lots of functional tests, however that is outside of the scope of this article.

Unit testing #

Using the unit tests we are testing our components in an independent manner so we can "ensure" that our piece of software is working correctly in an isolated way. Let´s see some examples of existing unit tests in the portal:

Unit test with no dependencies #

A good example of this type of test is the class AntlrCreoleParserTests. Writing this kind of unit tests is very easy because the piece of software we are testing, the Creole10Parser, has no external dependencies so we don´t need to mock (we will be speaking about mocks in a few moments) them. As we have said before, this is very easy, we just need to create a new class, and include our testing logic. Here is a small example

	public void testParseHeadingBlocksMultiple() {
		WikiPageNode wikiPageNode = getWikiPageNode("heading-10.creole");

		Assert.assertEquals(3, wikiPageNode.getChildASTNodesCount());

Note we are annotating the method with the JUnit @Test annotation. You can use the BaseTestCase class as the parent of your test class, it will depend on your needs.

Unit test with external dependencies #

All the tests are not as easy as the previous one because a piece of software may have a dependency on an external item in order to achieve its work. At this point, we want to test the behaviour of our code in an isolated way, assuming the code of our dependencies are working perfectly.

We use the mocking approach in order to achieve this goal through the PowerMock library plus its Mockito extesions Powermock. We are not covering how this libraries work but the use of them we are using at Liferay. Reference Card about Mockito

As an example, let´s analyze the main parts of the StripFilter class in order to explain how the tests work:

We apply the following two annotations to the test class definition:

	@PrepareForTest({CacheKeyGeneratorUtil.class, PropsUtil.class})

The @RunWith annotation tells Junit to use the PowerMockRunner instead of the default runner in order to allow the Powermock library to do its work. The @PrepareForTest annotation prepares both classes for being instrumented because the StripFilter has some external dependencies expressed as calls to static methods that we need to mock.

The next step is to write a new method and put our testing logic on it. Here is the sample structure of such a test that exercises the processCSS() method:

	public void testProcessCSS() throws Exception {
		// prepare data test

		// call the method we are testing 

		stripFilter.processCSS(null, null, charBuffer, stringWriter);

		// do the required asserts in order to verify everything is fine

When writing this type of test with mocks we often need to look into the code of the tested method. In this case we find out that the StripFilter class makes some calls to the method getCacheKeyGenerator on the class CacheKeyGeneratorUtil. As said before, we want to test the StripFilter in isolation, so we need to mock that invocation:


		new HashCodeCacheKeyGenerator()

We are describing how Powermock should behave when the method call CacheKeyGeneratorUtil.getCacheKeyGenerator() is invoked. Using the previous approach we are not invoking the real code but "mocking" it so we can assume everything we depend on is working fine because we are describing how it should work.

Once our testing logic has been executed we need to verify that everything has worked as expected, by doing some asserts on the data. In addition, we can include an extra assert like this:


which verifies if our "mocks" have worked as we have defined on the beginning of the test.

In some other cases (actually this should be the most common case), the methods that we are testing don't invoke static methods but rather method calls in object instances. Let's see an example of how to mock those object instances. In particular we will use the class TikaRawMetadataProcessor as an example. Here is a copy of the most relevant code of that class:

	public class TikaRawMetadataProcessor extends XugglerRawMetadataProcessor {

		. . .

		protected Metadata extractMetadata(
				InputStream inputStream, Metadata metadata)
			throws IOException {
			. . .

			_parser.parse(inputStream, contentHandler, metadata, parserContext);
			. . .

			return metadata;		

		private Parser _parser;

As we can see, it has a dependency on an instance of class Parser. In order to test the methods that depend on that instance we need to write some code and use some annotations that will replace that instance with a mock. Here is an example of how we would do that in our test class:

	class TikaRawMetadataProcessorTest {
		. . .

		private Parser _parser;

		private TikaRawMetadataProcessor _tikaRawMetadataProcessor =
			new TikaRawMetadataProcessor();	

The previous snippet show us how we need to configure our test:

  1. The first step is to tell the library that we are creating a mock in the _parser field. By applying @Mock, Mockito will automatically pupulate the field with a mock implementation of Parser.
  2. The second step requires injecting the previous mock into the metadata processor. By applying @InjectMocks, mockito will invoke the setters of TikaRawMetadataProcessor for each field in TikaRawMetadataProcessorTest that has the @Mock annotation.

At this point we are ready to mock all the required behaviour in order to validate our software component is working fine.

Since all of the above where just partial code excerpts you may want to take a look at the full test classes mentioned in this section. Here they are:

There are many more examples of unit tests around the source code of Liferay Portal. Search for the folder test/unit in the source folder of the portal.

Integration testing in portal #

The previous section describes how we can test certain classes or methods in isolation so we can ensure that the code of an specific class or method is working fine, in isolation of all its dependencies. But it is also very important to verify that our piece of software behaves as expected when interacting with its real dependencies: this is the goal of integration testing.

At the time of this writing, we are using a "testing out of the container approach" so we don't need to deploy the Liferay platform on an existing application server but we are starting the Spring application context. We know we can not write certain kind of tests witouth running inside an app server but our testing infrastructure is growing up and, hopefully, this will be solved in the near future; stay tuned.

Following the same pattern that the previous version, let's see some examples of how we can write integration tests:

Starting the Spring and OSGi application context #

All the integration tests require the Spring application context is fully up and running; so we need to start it before our test code gets executed. This is a very easy task, you only need to use a concrete JUnit rule: LiferayIntegrationTestRule.

Let's see how we can write an integration test:

	public class DiffImplTest {
		public static final LiferayIntegrationTestRule liferayIntegrationTestRule =
			new LiferayIntegrationTestRule();

		public void testEight() {

			List<DiffResult>[] actual = DiffUtil.diff(reader1, reader2);


As you can see, this is very easy, we only need to annotate our test class with the @Rule and @ClassRule annotation and use the rule we have spoken about just a few lines ago. In this case, the DiffUtil has an external dependency on the class Diff (which is injected through Spring).

The @DeleteAfterTestRun annotation #

The previous rule, in addition to configure and run the Spring and OSGi application contexts, allows us to use the DeleteAfterTestRun annotation.

This annotation delete the annotated field after the test is executed.

	public class BlogsEntryLocalServiceTest {

		public void setUp() throws Exception {	
			_group = GroupTestUtil.addGroup();


		private Group _group;


Others JUnit Rules #

The previous rule extends the class BaseTestRule, which allow to create extensions points that get executed at different points of the test execution process. These points are:

  1. Before Test: Execute code before each test is executed
  2. After Test: Execute code after each test is executed
  3. Before Class: Execute code before all the tests in a class are started
  4. After Class: Execute code after all the tests in a class are finished

Once I have created a new class extending the previous class, how can I use it on my tests? For that we are using the rule AggregateTestRule, that allow you to use more than one rule in the same test:

	public class MyIntegrationTest {

	public static final AggregateTestRule aggregateTestRule = new AggregateTestRule(

      new LiferayIntegrationTestRule(), Rule1.INSTANCE, Rule2.INSTANCE);

		public void testFoo() {					}	

At the time of this writing, the set of available rules are the following:

  • MainServletTestRule: Configure all the permissions and services before all the tests are executed. Destroy the services once all the tests have been executed. This rule also set up and configure the MainServlet before all the tests are executed.
  • TransactionalTestRule: Open a transaction before each test is executed and make a rollback once the test execution has finished. The database remains at the same status that it was at the begining of the test.
  • PersistenceTestRule: Achieves the same work than the previous rule but using a different approach. It is used only on generated code (by ServiceBuilder) so you don't need to use it.
  • SynchronousDestinationTestRule: This rule allow the MessageBus to be executed in Synchronous mode.It is required to annotate your class or your method with @Sync

Writing integration tests is generally easier than its unit counterparts because we don't need to simulate the original behaviour of the dependencies but using the real ones instead. Search for the folder test/integration in the portal source code and you will find lot of integration tests.

Integration testing in modules #

The previous section describes how we can create integration tests for portal. For create integration tests in modules we are NOT using the testing out of the container approach" so we are actually deploying the modules inside an app server where Liferay is running.

The principles when modules are tested are the same that the integration tests in portal, except that we need to use the annotation@RunWith(Arquillian.class)

	public class AssetPublisherExportImportTest {

	public static final AggregateTestRule aggregateTestRule = new AggregateTestRule(

      new LiferayIntegrationTestRule(), SynchronousDestinationTestRule.INSTANCE);


At the time of this writing, the only difference betwen the portal integration test and the modules integration tests are the following: 

  • MainServletTestRule: NEVER use this rule.
  • We need to add the junit bridge dependency to the ivy.xml file:
<?xml version="1.0"?>

	<info module="${}" organisation="com.liferay">
		<extends extendType="configurations,description,info" location="${sdk.dir}/ivy.xml" module="com.liferay.sdk" organisation="com.liferay" revision="latest.integration" />

		<artifact type="jar" />
		<artifact type="pom" />
		<artifact m2:classifier="sources" />

	<dependencies defaultconf="default">
		<dependency conf="test->default" name="com.liferay.arquillian.extension.junit.bridge" org="com.liferay" rev="1.0.0-SNAPSHOT" />

Initial Configuration #

Liferay's tests needs some configuration to be executed. #

You can set up some utility configs when building the portal. An example of this file could be:

source.formatter.excludes=/ #

You can set up your database configuration, or anothert portal properties in this file, that is only read by the test. An example of this file could be:


Running the tests #

Liferay's tests are executed through different Ant tasks.

Single class #

If you want to run a single test class you can use the


ant test-class -Dtest.class=NameOfTheClass
ant test-class -Dtest.class=NameOfTheClass -Djunit.debug=true (run the tests in debug mode: you can find the debug options in the file)

You don't need to include the qualified name of the class, only its name.

Individual methods #

If you want to run specific individual methods of the test class you can pass a comma delimited list of method names with the


ant test-method -Dtest.class=NameOfTheClass -Dtest.methods=someMethod,someOtherMethod

Batch several classes - 6.2 #

If you want to run a subset of several classes at once you can now pass a 'ant' style regex path pattern using the test.class property to the


ant test-class -Dtest.class=some/path/*Test

The above will execute all classes which match the path


Individual packages - 6.2 #

If you want to run all classes under a package at once you can now pass the package name using the package property to the


ant test-package -Dtest.package=com.liferay.portal.dao.orm

The above will execute all classes under the package 


IMPORTANT: You can run the tests for an specific subfolders of the Liferay source code moving to portal-impl, portal-service, . . . or you can run all tests of a kind from portal root source code folder. 

You can type in the selected source root folder

	ant test-integration


	ant test-unit

in order to run all the integration tests or the unit tests, respectively.

0 Anexos
11780 Visualizações
Média (1 Votar)
A média da avaliação é 1.0 estrelas de 5.