1

I use liferay service builder in my project and now i want to test *Util classes. It would be easy, but i don't know simple method of init environment. For example in ant testing with spring configuration from service.xml (auto generated) i use InitUtil.initWithSpring() for init beans, but get follow error:

[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 2,413 sec
[junit] Tests run: 1, Failures: 0, Errors: 1, Time elapsed: 2,413 sec
[junit] 
[junit] Testcase: testJournalArticleSearch(MTest):  Caused an ERROR
[junit] BeanLocator has not been set for servlet context My-portlet
[junit] com.liferay.portal.kernel.bean.BeanLocatorException: BeanLocator has not been set for servlet context My-portlet
[junit]     at com.liferay.portal.kernel.bean.PortletBeanLocatorUtil.locate(PortletBeanLocatorUtil.java:42)
[junit]     at com.my.service.EntityLocalServiceUtil.getService(EntityLocalServiceUtil.java:70)
[junit]     at MTest.setUp(MTest.java:21)

I've seen a few articles on this problem, but it doesn't work or i don't understand these articles... Somebody knows a simple solution to this problem?

dmitrievanthony
  • 1,501
  • 1
  • 15
  • 41
  • See if this answer helps: http://stackoverflow.com/a/11856097/468763, I have yet to try this out though. – Prakash K Aug 16 '12 at 07:07

2 Answers2

3

I'm writing this as an answer - it would be more a comment, but the formatting options and length of an answer are what I'm going after.

I frequently see that people have problems writing unit tests for generated code - and *Util, together with servicebuilder, sounds like generated code (*LocalServiceUtil) to me.

My advice is to rather test your *LocalServiceImpl code and trust that the codegenerator is correct (or trust that the codegenerator tests will catch mistakes in there, but this is outside of your scope). After all, the functionality that servicebuilder's *LocalServiceUtil classes deliver is an indirection that looks up the correct implementation (based on spring configuration) and delegate to it. There's no business logic in *LocalServiceUtil classes - this is in *LocalServiceImpl classes.

The next point is: Sometimes even the *Impl-classes are hard to test, because they reach out to other services, which would need to be mocked. In this case - to keep the unit tests readable and independent of the database - I'm proposing to test a layer of code that doesn't reach out to other services. To pick on the code I stole from this answer, here's how I'd rather test it, excluding UserLocalService from the equation (caution: pseudocode, never saw a compiler, I'm editing in this input field)

The code we're about to test is:

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

My Test for this would be ruling out UserLocalService:

@Test
public void testIsUserFullAge() throws Exception {
    //setup (having it here for brevity of the code sample)
    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);

    //run
    asertTrue(MyUserUtil.isUserFullAge(mockUserThatIsFullAge));
    asertFalse(MyUserUtil.isUserFullAge(mockUserThatIsNotFullAge));
}

The important part here is: Your code works on a User object, not on a user Id. Thus you don't need to test the lookup. If you desperately want to test the lookup as well (e.g. test on a broader scale), call it integration test. But don't complain if it breaks often because of some unrelated changes. Because now the reasons for your test to fail are of two different sources: The lookup fails OR your implementation is incorrect. You want your UNIT test to fail for exactly one of the reasons, e.g. immediately know what went wrong when the test fails, not start debugging.

Oh, and yes, that test will start to fail in 2018, in real life I'd test more corner cases, e.g. someone who turns 18 tomorrow or did so yesterday), but this is a different topic.

Community
  • 1
  • 1
Olaf Kock
  • 46,930
  • 8
  • 59
  • 90
  • Thank you very much, its perfectly work with portal LocalServiceUtil classes. If i understood, i must use com.liferay.util.bean.PortletBeanLocatorUtil instead of PortalBeanLocatorUtil or use powermochito? But i always getting 'null' or '0' from my methods despite the fact that it work's correctly on deployed project... – dmitrievanthony Aug 16 '12 at 11:40
  • Yes, you can make it work with LocalServiceUtil. But my point is that you shouldn't try this: It dilutes your unit tests. Either call them integration tests or accept that the code generator will not fail generating the correct wiring. What do you want to test? Your implemented functionality or the wiring generated by servicebuilder? I assume the first. If the wiring goes wrong: What part of your code would you change? – Olaf Kock Aug 16 '12 at 11:51
  • It's logical, but unfortunately in a situation, when a biggest part of code depends on the database access method and working with model this approach uncomfortable. So i can use only integration tests, correct? – dmitrievanthony Aug 16 '12 at 13:02
  • 1
    well - that's what you effectively do if you test more than one unit in your test: You test different layers of the application and IMHO this is an integration test. Yes, it's done with JUnit, but don't get tricked by that name. I do have a quite specific opinion on this - I'm aware of that, and I've tried to lay it out in that answer. As you've accepted it (thanks) it seems that it somehow resonated. I understand that you can't act on it tomorrow, but now you'll have some ideal to strive for ;) – Olaf Kock Aug 16 '12 at 20:47
  • Olaf, you're so right. in my company we have a lot of test cases written for junit. 'they' call it "unit tests" but they are actually integration tests. 'they' argue "it depends what you call 'unit'". while you and i think of a method as the 'unit', others consider certain flow/scenario a 'unit'. Spring framework for junit may add to the confusion as i think they provides mechanisms to re-create a whole network of beans so you can test one of them easily... Spring may call this "unit testing", i'm not sure... – inor Oct 14 '13 at 08:19
0

I use Mockito and PowerMock for mocking the Liferay services. The PowerMock allows to mock static methods like XXXLocalServiceUtil. In the linked answer from Prakash K, you can find detailed description: Testing for custom plugin portlet: BeanLocatorException and Transaction roll-back for services testing

Community
  • 1
  • 1
Mark
  • 17,887
  • 13
  • 66
  • 93
  • One little quastion, i using powermochito, as in the example, but always getting 'null' or '0' from methods from Util classes. Сan you help, what i doing wrong? – dmitrievanthony Aug 16 '12 at 13:11