简体   繁体   中英

Unit testing static method which uses a resource bundle

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.

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() );
    }
};

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.

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!

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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM