Prakash K
Prakash K

Reputation: 11698

Testing for custom plugin portlet: BeanLocatorException and Transaction roll-back for services testing

My Problems:

  1. I can test successfully for CRUD services operation. I was doing an insert on @Before [setUp()] and delete of same data on @After [tearDown()] but going forward I would need to support Transactions rather than writing code for insert and delete.

  2. I am successful in fetching single records of my entity but when I fire a search query or try to fetch more than one of my entities I get:

    com.liferay.portal.kernel.bean.BeanLocatorException: BeanLocator has not been set for servlet context MyCustom-portlet

I have followed some of the following links to set-up Junit with Liferay:

My Enviroment

It would be really helpful if I can get some idea as to how to go about using Transactions with JUnit (or at least some ideas as to how it works in liferay) and how to resolve the BeanLocatorException (or at least why would it be thrown)

Any help will be greatly appreciated.

Upvotes: 10

Views: 5045

Answers (2)

Mark
Mark

Reputation: 18807

I use for JUnit testing mockito framework and inject the services over PortalBeanLocatorUtil.setBeanLocator(...)-methode. I think that is clearly as to do this with spring configuration. Here you have full example how it can be used. The example is shot and that is good so, because the approach is simple and understandable.

package mst.unittest.example;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.junit.Before;
import org.junit.Test;

import com.liferay.portal.kernel.bean.BeanLocator;
import com.liferay.portal.kernel.bean.PortalBeanLocatorUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalService;
import com.liferay.portal.service.UserLocalServiceUtil;

import static org.junit.Assert.*;

import static org.mockito.Mockito.*;

/**
 * @author [email protected]
 */
public class MyUserUtilTest {


    private BeanLocator mockBeanLocator;

    @Before
    public void init()  {
        //create mock for BeanLocator, BeanLocator is responsible for loading of Services
        mockBeanLocator = mock(BeanLocator.class);
        //... and insert it in Liferay loading infrastructure (instead of Spring configuration)
        PortalBeanLocatorUtil.setBeanLocator(mockBeanLocator);
    }

    @Test
    public void testIsUserFullAge() throws PortalException, SystemException, ParseException {
        //setup
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd");
        Date D2000_01_01 = format.parse("2000_01_01");
        Date D1990_06_30 = format.parse("1990_06_30");
        UserLocalService mockUserLocalService = mock(UserLocalService.class);
        User mockUserThatIsFullAge = mock(User.class);
        when(mockUserThatIsFullAge.getBirthday()).thenReturn(D1990_06_30);
        User mockUserThatIsNotFullAge = mock(User.class);
        when(mockUserThatIsNotFullAge.getBirthday()).thenReturn(D2000_01_01);
        //overwrite getUser(...) methode so that wir get mock user-object with mocked behavior
        when(mockUserLocalService.getUser(1234567)).thenReturn(mockUserThatIsFullAge);
        when(mockUserLocalService.getUser(7654321)).thenReturn(mockUserThatIsNotFullAge);

        //load our mock-object instead of default UserLocalService
        when(mockBeanLocator.locate("com.liferay.portal.service.UserLocalService")).thenReturn(mockUserLocalService);


        //run
        User userFullAge = UserLocalServiceUtil.getUser(1234567);
        boolean fullAge = MyUserUtil.isUserFullAge(userFullAge);

        //verify
        assertTrue(fullAge);

        //run
        User userNotFullAge = UserLocalServiceUtil.getUser(7654321);
        boolean notfullAge = MyUserUtil.isUserFullAge(userNotFullAge);

        //verify
        assertFalse(notfullAge);
    }

}

class MyUserUtil {

    public static boolean isUserFullAge(User user) throws PortalException, SystemException {
        Date birthday = user.getBirthday();
        long years = (System.currentTimeMillis() - birthday.getTime()) / ((long)365*24*60*60*1000);
        return years > 18;
    }

}

You can use this approach also without mockito framework, then you must create the mock-classes like MockBeanLocator manually.

Approach with PowerMock

With PowerMock you can to abdicate BeanLocator because PowerMock allows to override static methods. Here the same example with PowerMock:

package mst.unittest.example;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalServiceUtil;

import static org.junit.Assert.*;

import static org.mockito.Mockito.*;

/**
 * @author Mark Stein
 *
 */

@RunWith(PowerMockRunner.class)
@PrepareForTest(UserLocalServiceUtil.class)
public class LiferayAndPowerMockTest {

    @Test
    public void testIsUserFullAge() throws PortalException, SystemException, ParseException {
        //setup
        SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd");
        Date D2000_01_01 = format.parse("2000_01_01");
        Date D1990_06_30 = format.parse("1990_06_30");
        User mockUserThatIsFullAge = mock(User.class);
        when(mockUserThatIsFullAge.getBirthday()).thenReturn(D1990_06_30);
        User mockUserThatIsNotFullAge = mock(User.class);
        when(mockUserThatIsNotFullAge.getBirthday()).thenReturn(D2000_01_01);

        //overwrite getUser(...) by UserLocalServiceUtil  methode so that wir get mock user-object with mocked behavior
        PowerMockito.mockStatic(UserLocalServiceUtil.class);
        when(UserLocalServiceUtil.getUser(1234567)).thenReturn(mockUserThatIsFullAge);
        when(UserLocalServiceUtil.getUser(7654321)).thenReturn(mockUserThatIsNotFullAge);

        //run
        boolean fullAge = MySecUserUtil.isUserFullAge(1234567);

        //verify
        assertTrue(fullAge);

        //run

        boolean notfullAge = MySecUserUtil.isUserFullAge(7654321);

        //verify
        assertFalse(notfullAge);
    }

}

class MySecUserUtil {

    public static boolean isUserFullAge(long userId) throws PortalException, SystemException {
        User user = UserLocalServiceUtil.getUser(userId);
        Date birthday = user.getBirthday();
        long years = (System.currentTimeMillis() - birthday.getTime()) / ((long)365*24*60*60*1000);
        return years > 18;
    }

}

Here you found PowerMock 1.4.12 with Mockito and JUnit including dependencies http://code.google.com/p/powermock/downloads/detail?name=powermock-mockito-junit-1.4.12.zip&can=2&q=

Upvotes: 5

gcesarmza
gcesarmza

Reputation: 301

Speculation: do you really need to test the transaction? Or just the business logic around the db access? Because if so, you could try writing the unit test with EasyMock (or similar), avoiding the access to the database yet testing the functionality

Upvotes: 2

Related Questions