2

Ive read so many articles on using Powermock and Mockito and tried so many different ways, but I still cant figure out the way to unit test the below static method.

public static Map<String, String> getEntries() {
    Map<String, String> myEntriesMap = new TreeMap<String, String>();
    ResourceBundle myEntries = ResourceBundle.getBundle(ENTRIES_BUNDLE);
    Enumeration<String> enumList = myEntries.getKeys();
    String key = null;
    String value = null;
    while (enumList.hasMoreElements()) {
        key = enumList.nextElement().toString();
        value = myEntries.getString(key);
        myEntriesMap.put(key, value);
    }
    return myEntriesMap;
}

The code is part of a (legacy) class containing about 30 static methods like this and refactoring is not really an option. Similarly in some other static methods, DBconnections are being retrieved.

Eg : How do I mock the resource bundle ENTRIES_BUNDLE and unit test this method ? I am looking for a pattern that could be applied generally to all the static methods.

user2706486
  • 21
  • 1
  • 1
  • 2
  • 1
    Yeah, this method isn't really unit testable in its present form. Not because it's static, but because of the call to `ResourceBundle.getBundle`. In theory, you could do it with PowerMock, if you really had to, although refactoring would be a much better option. But I look at your question and I wonder why you are unit testing a legacy class. Surely, if it's a legacy class, then most of the opportunity to benefit from unit testing has already passed? And if you're not allowed to change it, then what will you do if your testing uncovers a bug? – Dawood ibn Kareem Aug 22 '13 at 10:10
  • Thanks for your answer. I agree with you completely about the value derived by writing Junits now. The thing is we have this project and part of the code is covered by JUnits and part of the code is not. The idea is to bring the whole code upto date with Junits and cover new development also with unit test cases. The code being legacy and being tested and stable, is the reason we dont want to refactor it (at least at this point in time). But having said that, and looking at your comment, it would be very helpful if you could point me towards how to do this with Powermock. – user2706486 Aug 22 '13 at 11:25
  • I am going straight to mock hell for this - but https://code.google.com/p/powermock/wiki/MockitoUsage13 has information about how to mock static methods. About one screen down from the top. But honestly, try Rogério's way first if you can. – Dawood ibn Kareem Aug 22 '13 at 19:07
  • I already tried Rogério's method before posting the question. But I was looking for a pattern which I could use for other conditions as well like DB connection etc. But based on your suggestion / idea Im seriously considering refactoring some of the original code. Lets see what management has to say about that. But I may see you in the mock hell after all ;-) Thanks for your time and help ! – user2706486 Aug 23 '13 at 13:33

4 Answers4

10

Use ResourceBundle.getBundle( String, ResourceBundle.Control ) to get ResourceBundle to cache a bundle for the given String. You can subclass ResourceBundle.Control to provide any type of bundle your heart desires.

@Test
public void myTest()
{
    // In your Test's init phase run an initial "getBundle()" call
    // with your control.  This will cause ResourceBundle to cache the result.
    ResourceBundle rb1 = ResourceBundle.getBundle( "blah", myControl );

    // And now calls without the supplied Control will still return
    // your mocked bundle.  Yay!
    ResourceBundle rb2 = ResourceBundle.getBundle( "blah" );
}

Here's the subclassed Control:

ResourceBundle.Control myControl = new ResourceBundle.Control()
{
    public ResourceBundle newBundle( String baseName, Locale locale, String format,
            ClassLoader loader, boolean reload )
    {
        return myBundle;
    }
};

And here's one way to mock the ResourceBundle (filling up the TreeMap with keys/values as needed for the unit tests left as an exercise for the reader):

ResourceBundle myBundle = new ResourceBundle()
{
    protected void setParent( ResourceBundle parent )
    {
      // overwritten to do nothing, otherwise ResourceBundle.getBundle(String)
      //  gets into an infinite loop!
    }

    TreeMap<String, String> tm = new TreeMap<String, String>();

    @Override
    protected Object handleGetObject( String key )
    {
        return tm.get( key );
    }

    @Override
    public Enumeration<String> getKeys()
    {
        return Collections.enumeration( tm.keySet() );
    }
};
Julius Musseau
  • 4,037
  • 23
  • 27
6

You don't need to mock the ResourceBundle.getBundle method. Simply create a ".properties" file at the proper place in the test source tree, instead. This will still be a perfectly good and useful unit test.

Rogério
  • 16,171
  • 2
  • 50
  • 63
  • 1
    +1 this is nice and clean. The only problems I see are if the tests need a different properties file from the application code, or if different tests need different properties files from each other. If this proves to be an issue, you'll want to write tests that set up Properties objects, then write them into the "live" space. – Dawood ibn Kareem Aug 22 '13 at 19:10
1

We had a simular issue mocking the ResourceBundle.getString() method.

java.util.MissingResourceException: Can't find resource for bundle $java.util.ResourceBundle$$EnhancerByMockitoWithCGLIB$$e9ea44f0, key name

Our problem was that the method was final, which makes it impossible for mockito to mock the method.

Instead we used this soultion: https://code.google.com/p/powermock/wiki/MockSystem

Note that @PrepareForTest({ClassThatCallsTheSystemClass.class}) is NOT the ResourceBundle class!

Tim
  • 11
  • 1
0

If you are using the following libraries: mockito-all and jmockit do this steps:

Let say that you want to mock the method yyyy from the xxxx.class

@MockClass(realClass = xxxx.class)
public static class MyClass {
     @Mock
     public static void yyyy(){
          ......
     }
}

in your test:

@Test
public void test() {
     Mockit.setUpMock(MyClass.class);
}
cristi
  • 361
  • 3
  • 9
  • To make this a good answer, you should really describe the mocking framework that you're using. The OP asked about Mockito and PowerMock - this is neither of those. What do we have to add to our build to make this test work? – Dawood ibn Kareem Aug 22 '13 at 19:12
  • I am using mockito-all and jmockit libraries – cristi Aug 23 '13 at 13:45