Csaba Faragó Csaba Faragó - 4 months ago 17
Java Question

Check number of invocations within class in case of JUnit testing

I have a code which calculates something, caches is, and if already calculated, then reads from the cache; similar to this:

public class LengthWithCache {
private java.util.Map<String, Integer> lengthPlusOneCache = new java.util.HashMap<String, Integer>();

public int getLenghtPlusOne(String string) {
Integer cachedStringLenghtPlusOne = lengthPlusOneCache.get(string);
if (cachedStringLenghtPlusOne != null) {
return cachedStringLenghtPlusOne;
}
int stringLenghtPlusOne = determineLengthPlusOne(string);
lengthPlusOneCache.put(string, new Integer(stringLenghtPlusOne));
return stringLenghtPlusOne;
}

protected int determineLengthPlusOne(String string) {
return string.length() + 1;
}
}


I want to test if function
determineLengthPlusOne
has been called adequate number of times, like this:

public class LengthWithCacheTest {
@Test
public void testGetLenghtPlusOne() {
LengthWithCache lengthWithCache = new LengthWithCache();

assertEquals(6, lengthWithCache.getLenghtPlusOne("apple"));
// here check that determineLengthPlusOne has been called once

assertEquals(6, lengthWithCache.getLenghtPlusOne("apple"));
// here check that determineLengthPlusOne has not been called
}
}


Mocking class
LengthWithCache
does not seem a good option, as I want to test their functions. (According to my understanding we mock the classes used by the tested class, and not the tested class itself.) Which is the most elegant solution for this?

My first idea was to create another class
LengthPlusOneDeterminer
containing function
determineLengthPlusOne
, add pass it to function
getLenghtPlusOne
as parameter, and mock
LengthPlusOneDeterminer
in case of unit testing, but that seems a bit strange, as it has unnecessary impact on the working code (the real clients of class
LengthWithCache
).

Basically I am using Mockito, but whatever mock framework (or other solution) is welcome! Thank you!

Answer

Most elegant way would be to create a separate class that does the caching and decorate with it the current class (after removal of the caching), this way you can safely unit test the caching itself without interfering with functionalities of the base class.

public class Length {
    public int getLenghtPlusOne(String string) {
        int stringLenghtPlusOne = determineLengthPlusOne(string);
        lengthPlusOneCache.put(string, new Integer(stringLenghtPlusOne));
        return stringLenghtPlusOne;
    }

    protected int determineLengthPlusOne(String string) {
        return string.length() + 1;
    }
}

public class CachedLength extends Length {
    private java.util.Map<String, Integer> lengthPlusOneCache = new java.util.HashMap<String, Integer>();

    public CachedLength(Length length) {
        this.length = length; 
    }

    public int getLenghtPlusOne(String string) {
        Integer cachedStringLenghtPlusOne = lengthPlusOneCache.get(string);
        if (cachedStringLenghtPlusOne != null) {
            return cachedStringLenghtPlusOne;
        }
        return length.getLenghtPlusOne(string);
    }
}

Then you can easily test the caching my injecting a mocked Length:

Length length = Mockito.mock(Length.class);
CachedLength cached = new CachedLength(length);
....
Mockito.verify(length, Mockito.times(5)).getLenghtPlusOne(Mockito.anyInt());
Comments