I use Mockito and JUnit 5. I have a small program that counts distinct characters in a string. I have a cache in the form of a Map and the result is picked from the cache if the input string had been processed before.
My goal is to write a test using Mockito to verify that the result is picked from the cache if the string had been processed before. I can't figure out how to use the verify Mockito method properly.
Here is my code:
public class CounterDecorator implements CharCounter{
Cache cache;
CharCounter counter;
public CounterDecorator(Cache cache, CharCounter counter) {
this.cache = cache;
this.counter=counter;
}
#Override
public Map<Character, Integer> count(String text) {
if(!cache.contains(text)) {
System.out.println("New entry in cache");
cache.putText(text, counter.count(text));
}
return cache.getText(text);
}
}
public class Cache {
private Map <String, Map<Character, Integer>> cache = new HashMap <>();
public void putText(String text, Map <Character, Integer> result) {
cache.put(text, result);
}
public Map<Character, Integer> getText(String text) {
return cache.get(text);
}
public boolean contains(String text) {
return cache.containsKey(text);
}
}
And tests:
#ExtendWith(MockitoExtension.class)
class DecoratorTest {
#Mock
Cache mcache;
#Mock
CharCounter mcharcounter;
#InjectMocks
CounterDecorator decorator;
#Test
void testWhenCacheIsNotEmpty() {
Map<Character, Integer> testMap = Collections.emptyMap();
verify(mcache, atLeastOnce()).putText("some string", testMap);
}
}
I am sure I use Mockito wrong. But I can't figure out how to resolve my problem. Thank you in advance.
EDIT. I edited my tests part a bit: removed that confusing map instantiation.
Related
It's complicated for me to articulate a proper title for this. But an example should make it far simpler. Suppose I have this:
final class Cache {
private static final ConcurrentHashMap<String, List<String>> CACHE = ...
static List<String> byName(String name) {
return CACHE.computeIfAbsent(name, x -> // some expensive operation)
}
}
The idea is probably trivial, this acts as a LoadingCache, much like guava or caffeine (in reality it is more complicated, but that is irrelevant to the question).
I would like to be able to tell if this was the first load into the CACHE, or it was a read of an existing mapping. Currently, I do this:
final class Cache {
private static final ConcurrentHashMap<String, List<String>> CACHE = ...
static List<String> byName(String name) {
boolean b[] = new boolean[1];
List<String> result = CACHE.computeIfAbsent(name, x -> {
b[0] = true;
// some expensive operation)
});
if(b[0]) {
// first load into the cache, do X
} else {
// do Y
}
return result;
}
}
This works, but I am afraid I am missing something that ConcurrentHashMap can offer for me that would allow me to do the same. Thank you.
If you want to avoid your single-element array to pass data out of the lambda (which I would rather do with an AtomicReference or AtomicBoolean), you could use a stateful callback object. It doesn't change the behavior or design of your code, but could be considered a little bit cleaner and more OOP-y.
class LoadingAction<K, V> {
private boolean called = false;
public V load(final K key) {
called = true;
// load data
return ...;
}
public void executePostLoad() {
if (called) {
// loaded into cache, do X
} else {
// do Y
}
}
}
final class Cache {
private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();
static List<String> byName(String name) {
final LoadingAction<String, List<String>> loader = new LoadingAction<>();
final List<String> result = CACHE.computeIfAbsent(name, loader::load);
loader.executePostLoad();
return result;
}
}
Or turn it inside-out:
class Loader<K, V> {
private boolean called = false;
public V load(final Map<K, V> map, final K key) {
final V result = map.computeIfAbsent(key, this::load);
this.executePostLoad();
return result;
}
private V load(final K key) {
called = true;
// load data
return ...;
}
private void executePostLoad() {
if (called) {
// loaded into cache, do X
} else {
// do Y
}
}
}
final class Cache {
private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();
static List<String> byName(String name) {
final Loader<String, List<String>> loader = new Loader<>();
return loader.load(CACHE, name);
}
}
Construction and loading could be encapsulated in a static method:
class Loader<K, V> {
private boolean called = false;
public static <K, V> V load(final Map<K, V> map, final K key) {
final Loader<K, V> loader = new Loader<>();
return loader.doLoad(map, key);
}
private V doLoad(final Map<K, V> map, final K key) {
final V result = map.computeIfAbsent(key, this::load);
this.executePostLoad();
return result;
}
private V load(final K key) {
called = true;
// load data
return ...;
}
private void executePostLoad() {
if (called) {
// loaded into cache, do X
} else {
// do Y
}
}
}
final class Cache {
private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();
static List<String> byName(String name) {
return Loader.load(CACHE, name);
}
}
I can think of a couple of ways to do this using the ConcurrentHashMap API. Both reply on using a mapping function that has side-effects. The idea is that the function makes a record of whether it was called, or what arguments it was called with.
The spec for computeIfAbsent says that the mapping function is only called if the key is absent. Alternatively, the spec for compute says that the mapping function is called with a null argument if the key is argument. In either case, if you record what happened in the mapper function via a side-effect on (say) a field of the mapper function/object, you can determine if the cache entry was already present or not.
To make this thread-safe, you need to create a fresh (thread-confined) instance of the mapper function.
I am new to JMockit. I have recently tried writing unit tests where I have to test a system method that takes an argument. Inside the method under test the argument instance chains multiple of its methods to return a Collection object.
The code below simulates the exact situation I am facing. I am using jmockit-1.9
The system under test
public class SystemUnderTest {
public void doSomething(Dependency dependency) {
List<String> list = dependency.getMap().get("some-key");
System.out.println("got list -> " + list);
}
}
Dependency class
public class Dependency {
private Map<String, List<String>> map;
public Map<String, List<String>> getMap() {
return map;
}
public void setMap(Map<String, List<String>> map) {
this.map = map;
}
}
Test
#Test
public void testDoSomething(
#Mocked Dependency dependency,
#Mocked Map<String, List<String>> map,
#Mocked List<String> list
) {
new NonStrictExpectations() {{
dependency.getMap(); result = map;
map.get(anyString); result = list; //exception occurs here
}};
SystemUnderTest sut = new SystemUnderTest();
sut.doSomething(dependency);
}
When I run the test, I get the following exception
java.lang.IllegalArgumentException: Invalid return value for method returning class java.util.HashMap
at this line
map.get(anyString); result = list;
As it can be clearly see, the return type of map.get() should be a List but it says that the method is returning a HashMap.
What am I doing wrong?
Any help would be appreciated;
I have the following class
public class One {
private Map<String, String> nodes = new HashMap<String, String>();
public void addNode(String node, String nodefield){
this.nodes.put(node, nodefield);
}
}
I want to write a test class to test the addNode method and have the following:
#RunWith(MockitoJUnitRunner.class)
public class OneTest {
#InjectMocks
private One one = new One();
#Mock
Map<String, String> nodes;
#Test
public void testAddNode(){
one.addNode("mockNode", "mockNodeField");
Mockito.verify(nodes).put("mockNode","mockNodeField");
}
}
which works. But I was wondering if there is a way to do it without using #InjectMocks like the following
public class OneTest {
private One one;
#Test
public void testAddNode(){
Map<String, String> nodes = Mockito.mock(Map.class);
one = Mockito.injectmocks(One.class, nodes); // or whatever equivalent methods are
one.addNode("mockNode", "mockNodeField");
Mockito.verify(nodes).put("mockNode","mockNodeField");
}
}
How about change the class by injecting the map as a dependency? This makes it easier to test and gives you the added benefit of being able to use an implementation of the Map interface, for example:
public class One {
private Map<String, String> nodes;
public One(Map<String, String> nodes) {
this.nodes = nodes;
}
public void addNode(String node, String nodefield){
this.nodes.put(node, nodefield);
}
}
Then to test:
Map mockMap = Mockito.mock(Map.class);
One one = new One(mockMap);
one.addNode("mockNode", "mockNodeField");
Mockito.verify(mockMap).put("mockNode","mockNodeField");
Okay I figured it out by using PowerMockito instead of normal Mockito.
public class OneTest {
private One one;
#Test
public void testAddNode(){
HashMap nodes = PowerMockito.mock(HashMap.class);
PowerMockito.whenNew(HashMap.class).withNoArguments().thenReturn(nodes);
One one = new One();
one.addNode("mockNode", "mockNodeField");
Mockito.verify(nodes).put("mockNode","mockNodeField");
}
}
However, I don't really know what PowerMockito does that Mockito doesn't do to make it work though.
I am struggling to find a way to dispatch this to functions in java8
Person p = registry.getPerson();
if (field == Field.LASTNAME) {
p.setLastName(str);
}
if (field == Field.FIRSTNAME) {
p.setFirstName(str);
}
if (field == Field.MIDDLENAME) {
p.setMiddleName(str);
}
My idea is to use some kind of function dispatch table to replace the if statements in the case of more cases:
Map<Integer, Function> map = new HashMap<Integer, Function>
static {
map.put(1, new Function<String, String>() {
#Override
public Object apply(String str) {
person.setLastName(str);
return str;
}
}
}
But the code cannot compile, because i need to pass the person object some place. Anyone knows a pattern for this?
Assuming Field is an enum, you can add BiConsumer<Person,String> as an enum field:
class Person {
static enum Field {
FIRSTNAME(Person::setFirstName),
MIDDLENAME(Person::setMiddleName),
LASTNAME(Person::setLastName)
;
private BiConsumer<Person, String> setter;
private Field(BiConsumer<Person, String> setter) {
this.setter = setter;
}
}
public void set(Field field, String str) {
field.setter.accept(this, str);
}
......
}
Instead of storing Function<String,String>, you can store BiFunction<Person,String,String> and pass the Person instance in as a parameter.
Map<Integer, BiFunction<Person,String,String>> map =
new HashMap<Integer, BiFunction<Person,String,String>>();
static {
map.put(1, (person, str)->person.setLastName(str));
}
In the interest of simplicity, you could also just store a List of the functions, if you're just going to index them by an integer, it's faster for random access and makes for less complicated generic code:
List<BiFunction<Person,String,String>> list = new ArrayList<BiFunction<Person,String,String>>();
static {
list.add((person, str)->person.setLastName(str));
}
I need a mapping from a list of keys to a value. I know I could write my own code like this:
Map<Person, Map<Daytime, Map<Food, Integer>>> eaten = ...;
Now I want to have some get and put methods like these:
Integer numberOfEggsIAteInTheMorning = eaten.get(me, morning, scrambledEggs);
eaten.put(me, evening, scrambledEggs, 1);
Do you know of an existing class that has this kind of API? I'm too lazy of writing it myself. ;)
If you look for a more generic approach, and you might have more than 2 or 3 'chain steps', I would suggest in applying some different structural approach, rather than sticking to using only basic collection classes. I have feeling that Composite Pattern could be the right choice if it's correctly applied.
EDIT: due to example requested
The full example would be somewhat time consuming, so let me just explain my idea with dirty Java/pseudocode mix (I'm not even sure if I've missed something!!!). Let's consider we have class BaseMap:
abstract class BaseMap {
public abstract Object getValue(Object.. keys);
public abstract void putValue(Object value, Object.. keys);
}
Then we could have ObjectMap that would be the 'leaf' of our composite structure:
class ObjectsMap extends BaseMap {
private Map<Object, Object> map = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length == 1
return map.get(keys[0]);
}
public void putValue(Object value, Object.. keys) {
// assert that keys.length = 1
map.put(keys[0], value);
}
}
And the actual composite would be as such:
class CompositeMap extends BaseMap {
private Map<Object, BaseMap> compositeMaps = new [...]
public Object getValue(Object.. keys) {
// assert that keys.length > 1
return compositeMap.get(keys[0]).getValue(/* System.arrayCopy => subset of elements {keys_1, .. ,keys_max} */);
}
public void putValue(Object value, Object.. keys) {
// assert keys.length > 1
BaseMap newMap = null;
if (keys.length = 2) -> newMap = new ObjectsMap()
else newMap = new CompositeMap();
newMap.putValue(value, /*subset of keys {keys_1, .. , keys_max}*/);
}
}
You can use org.apache.commons.collections.keyvalue.MultiKey for that: Map<Multikey, Object>
It would be hard to implement a general chained map.
How would the declaration of the class look like? (You can't have a variable number of type parameters.
class ChainedMap<K1..., V>
Another option would be to have a ChainedMapUtil class that performs put / get recursively.
Here is an example of a recursive get. (Quite ugly solution though I must say.)
import java.util.*;
public class Test {
public static Object chainedGet(Map<?, ?> map, Object... keys) {
Object k = keys[0];
if (!map.containsKey(k)) return null;
if (keys.length == 1) return map.get(k);
Object[] tailKeys = Arrays.copyOfRange(keys, 1, keys.length);
return chainedGet((Map<?,?>) map.get(k), tailKeys);
}
public static void main(String[] arg) {
Map<String, String> m1 = new HashMap<String, String>();
m1.put("ipsum", "dolor");
Map<Integer, Map<String, String>> m2 =
new HashMap<Integer, Map<String, String>>();
m2.put(17, m1);
Map<String, Map<Integer, Map<String, String>>> chained =
new HashMap<String, Map<Integer, Map<String, String>>>();
chained.put("lorem", m2);
System.out.println(chainedGet(chained, "lorem", 17, "ipsum")); // dolor
System.out.println(chainedGet(chained, "lorem", 19, "ipsum")); // null
}
}
If you are going to write your own, I would suggest
eaten.increment(me, evening, scrambledEggs);
You could use a composite key
eaten.increment(Key.of(me, evening, scrambledEggs));
(TObjectIntHashMap supports increment and adjust)
You may not even need a custom key.
eaten.increment(me + "," + evening + "," + scrambledEggs);
It is fairly easy to decompose the key with split()
I once made a map using 3 keys just for fun.May be you can use it instead of using chained maps:
public class ThreeKeyMap<K1,K2,K3,V>{
class wrap{
K1 k1;
K2 k2;
K3 k3;
public wrap(K1 k1,K2 k2,K3 k3) {
this.k1=k1;this.k2=k2;this.k3=k3;
}
#Override
public boolean equals(Object arg0) {
// TODO Auto-generated method stub
wrap o=(wrap)arg0;
if(!this.k1.equals(o.k1))
return false;
if(!this.k2.equals(o.k2))
return false;
if(!this.k2.equals(o.k2))
return false;
return true;
}
#Override
public int hashCode() {
int result=17;
result=37*result+k1.hashCode();
result=37*result+k2.hashCode();
result=37*result+k3.hashCode();
return result;
}
}
HashMap<wrap,V> map=new HashMap<wrap, V>();
public V put(K1 k1,K2 k2,K3 k3,V arg1) {
return map.put(new wrap(k1,k2,k3), arg1);
}
public V get(Object k1,Object k2,Object k3) {
return map.get(new wrap((K1)k1,(K2)k2,(K3)k3));
}
public static void main(String[] args) {
ThreeKeyMap<Integer,Integer,Integer,String> birthDay=new ThreeKeyMap<Integer, Integer, Integer, String>();
birthDay.put(1, 1,1986,"Emil");
birthDay.put(2,4,2009, "Ansih");
birthDay.put(1, 1,1986,"Praveen");
System.out.println(birthDay.get(1,1,1986));
}
}
UPDATE:
As #Arturs Licis suggested.I looked up in net for composite pattern and I wrote a sample using it.I guess this is composite..Please comment if it is not so.
Person class:
public class Person {
private final String name;
private Map<Time, Food> map = new HashMap<Time, Food>();
public Person(String name) {
this.name = name;
}
void addTimeFood(Time time, Food food) {
map.put(time, food);
}
public String getName() {
return name;
}
Food getFood(Time time) {
Food tmp = null;
return (tmp = map.get(time)) == null ? Food.NoFood : tmp;
}
// main to test the person class
public static void main(String[] args) {
Person p1 = new Person("Jack");
p1.addTimeFood(Time.morning, Food.Bread);
p1.addTimeFood(Time.evening, Food.Chicken);
Person p2 = new Person("Jill");
p2.addTimeFood(Time.morning, Food.Egg);
p2.addTimeFood(Time.evening, Food.Rice);
Map<String, Person> map = new HashMap<String, Person>();
map.put(p1.getName(), p1);
map.put(p2.getName(), p2);
System.out.println(map.get("Jack").getFood(Time.evening));
}
#Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append(name).append("\n");
b.append(map);
return b.toString();
}
}
Food class:
public enum Food {
Rice,
Egg,
Chicken,
Bread,
NoFood;
}
Time class:
public enum Time {
morning,
evening,
night
}