I have a problem mocking an Iterable class combined with a call of spliterator(). It all works fine when calling spliterator once, but the second call returns no values.
As the mock always returns the same Spliterator instance, I assume that the state is not reset. Is there a way to do this?
This is the smallest example I could give
The call mapStringToHash is a Lib in real life and can't be changed.
MyIterable is also no object under my control.
package net.test;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class SpliteratorTest {
class MyIterable<T> implements Iterable<T> {
private List<T> list;
MyIterable(List<T> list) {
this.list = new ArrayList<>(list);
}
#Override
public Iterator<T> iterator() {
return list.iterator();
}
#Override
public Spliterator<T> spliterator() {
return list.spliterator();
}
}
// this a library method
private Stream<Integer> mapStringToHash(final MyIterable<String> myIterable) {
return StreamSupport.stream(myIterable.spliterator(), false).map(String::hashCode);
}
#Test
public void testSeveralSpliteratorCalls() {
MyIterable myIterable = givenMyIterableMock("a", "b", "c");
Stream<Integer> myIterableHash1 = mapStringToHash(myIterable);
assertThat(myIterableHash1.count(), is(3L));
Stream<Integer> myIterableHash2 = mapStringToHash(myIterable);
assertThat(myIterableHash2.count(), is(3L));
}
private MyIterable givenMyIterableMock(String... values) {
MyIterable myIterable = mock(MyIterable.class);
Spliterator myIterableSpliterator = Arrays.stream(values)
.collect(toList())
.spliterator();
doReturn(myIterableSpliterator).when(myIterable).spliterator();
return myIterable;
}
}
It turns out it's not as circumvent as I thought. It can be done using a custom Answer implementation, but since Answer is a functional interface, the following suffices:
Mockito.when(myIterable.spliterator()).then(invocation -> Arrays.spliterator(values));
Related
In my implementation I'm grabbing data from ElasticSearch via ElasticsearchTemplate#stream() which returns CloseableIterator<>.
I'd like to mock this method by doing:
List<MyClass> foo = new ArrayList<>(); // and then I add some elements
when(mockTemplate.stream(any(SearchQuery.class), eq(MyClass.class))
.thenReturn(foo.iterator());
but iterator() returns Iterator<MyClass> which is not CloseableIterator<MyClass> and I have no clue how to cast it or find a workaround.
org.springframework.data.util.CloseableIterator.CloseableIterator from Spring and java.util.Iterator from JDK are close enough interfaces : indeed CloseableIterator has Iterator as parent interface.
Which means that you can substitute a Iterator by a CloseableIterator but not the reverse : what you need.
While you cannot return an Iterator in your mock record, you can still convert an Iterator to a CloseableIterator. Which is not very hard thank to method delegation while it still requires a small amount of boilerplate.
Example :
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.util.CloseableIterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
#ExtendWith(MockitoExtension.class)
public class MockCloseableIteratorTest {
#Mock
ElasticsearchTemplate mockTemplateMock;
#Test
void foo() {
List<String> foo = new ArrayList<>();
// and then I add some elements
Mockito.when(mockTemplateMock.stream(any(SearchQuery.class), eq(String.class)))
.thenReturn(createCloseableIterator(foo.iterator()));
}
private <T> CloseableIterator<T> createCloseableIterator(Iterator<T> iterator) {
return new CloseableIterator<T>() {
#Override public void close() {
}
#Override public boolean hasNext() {
return iterator.hasNext();
}
#Override public T next() {
return iterator.next();
}
};
}
}
For those working on Kotlin, following will be the syntax for createCloseableIterator method:
private fun <T> createCloseableIterator(iterator: Iterator<T>): CloseableIterator<T>? =
object : CloseableIterator<T> {
override fun close() {}
override fun hasNext() = iterator.hasNext()
override fun next() = iterator.next()
override fun remove() {}
}
#MockK
private lateinit var closeableIterator: CloseableIterator<YourClass>
fun() {
every { closeableIterator.hasNext() } returnsMany listOf(true, false)
every { closeableIterator.next() } returnsMany listOf(YourClass())
}
For another Kotlin version that worked for me.
I got an error when I tried to test 2 ArraysLists.
It seems the error is saying "toArray() is undefined" at my removeEndWith_at method. Could you guys give me an advice how to test these 2 ArraysList?
Thanks.
Java version: jdk-10.0.2
JUnit:5
[ArrayListIterator Class]
import java.util.Iterator;
import java.util.List;
public class ArrayListIterator {
/**
* #param wordsAl : list of words
*/
public List<String> removeEndWith_at(List<String> wordsAl) {
Iterator<String> iterator = wordsAl.iterator();
while (iterator.hasNext()) {
if (iterator.next().endsWith("at"))
iterator.remove();
}
return wordsAl;
}
}
[ArrayListIteratorTest Class]
import static org.junit.Assert.assertArrayEquals;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;
class ArrayListIteratorTest {
ArrayListIterator alIterator = new ArrayListIterator();
List<String> actualWords = Arrays.asList("Apple", "Bat", "Orange", "Cat");
#Test
void testremoveEndWith_at() {
actualWords = alIterator.removeEndWith_at(actualWords);
List<String> expectedvalue = Arrays.asList("Apple", "Orange");
assertArrayEquals(expectedvalue.toArray(), actualWords.toArray());
}
}
Look at the
remove() on List created by Arrays.asList() throws UnsupportedOperationException
Arrays.asList()
method just creates a wrapper around the original elements and on this wrapper are not implemented methods which changes its size.
Also look at my implementation of method removeEndWith_at. It is simpler than yours version
/**
* #param wordsAl : list of words
*/
public List<String> removeEndWith_at(List<String> wordsAl) {
wordsAl.removeIf(s -> s.endsWith("at"));
return wordsAl;
}
When comparing two instances of List<String> using Jupiter Assertion API, give assertLinesMatch a try.
I have recently tried programming various versions of Iterator and Observer Patterns in Java, but currently I'm stuck at the implementation of both in one project:
What I'm trying to accomplish is to iterate through an ArrayList, which contains elements of type and after each iteration-process to inform the Observer about the change and the containing element.
Also I want to be able to choose the "type" of iterator in my separate classes
Edit: I finally finished the project, hopefully this is the correct version of a "Observer Iterator", any tips on how to improve the code are greatly appreciated, but thanks to everybodys' encouraging tips.
Code:
First of all, the helper class for the Observer interface, which implements the update method. Changes with every iteration process and prints out the element.
package iteratorObserver;
import java.util.List;
public class ConcreteObserver implements Observer{
#Override
public void update(String element) {
System.out.println("The current element is: " + element);
}
}
The actual Observer interface:
package iteratorObserver;
public interface Observer<E> {
void update(String element);
}
Following that, the same by using a ConcreteSubject and an abstract class Subject
The observer pattern requires the getState and setState, so I integrated both into the next method. Getting the element by iterator.next aswell as setting it.
package iteratorObserver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ConcreteSubject<E> extends Subject implements IteratorStandardFilter<E>{
List<String> listElements = new ArrayList<String>();
private int index;
private Iterator<E> iterator;
public ConcreteSubject(Iterator<E> iterator, List<String> listElements){
index = 0;
this.iterator = iterator;
this.listElements = listElements;
}
public void addElement(String element) {
listElements.add(element);
notifyObservers(element);
}
// check the size of the elements list and return if there are still
// elements left out
#Override
public boolean hasNext() {
if ((listElements.size() == index))
return false;
return true;
}
// iterate to the next element, not sure if it's overridden, since the
// actual next method of the iterator is still in use
#Override
public E next() {
E result = iterator.next();
index++;
notifyObservers((String) result);
return result;
}
}
Subject class, only used for the standard observer methods
package iteratorObserver;
import java.util.ArrayList;
import java.util.List;
public class Subject {
public List<Observer> listObservers = new ArrayList<Observer>();
public void addObserver(Observer observer){
listObservers.add(observer);
}
public void removeObserver(Observer observer){
listObservers.remove(observer);
}
public <E> void notifyObservers(String element){
for(Observer observerHelp : listObservers){
observerHelp.update(element);
}
}
}
Since i want to add various filters, not only the standard list-iterator, i added a class IteratorStandardFilter, as you can see here:
package iteratorObserver;
import java.util.Iterator;
public interface IteratorStandardFilter<E> extends Iterator<E> {
public boolean hasNext();
public E next();
}
And finally, the Test method, the main:
package iteratorObserver;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class iteratorObserverMain {
public static void main(String [] args){
List<String> listElements = new ArrayList<String>();
listElements.add("hello");
listElements.add("there");
listElements.add("everybody");
listElements.add("it");
listElements.add("works");
listElements.add("Thanks for your help. Nice and encouraging forum");
Iterator<String> iterator = listElements.iterator();
ConcreteSubject<String> concreteSubject = new ConcreteSubject<String>(iterator, listElements);
concreteSubject.addObserver(new ConcreteObserver());
while(concreteSubject.hasNext()){
concreteSubject.next();
}
}
}
I am trying to set items to a tableview but the setitems method expects an observablelist while I have an observableset in my model.The FXCollections utility class does not have a method for creating an observable list given an observable set.I tried casting but that caused a class cast exception (as expected).
Currently I am using this kind of code
new ObservableListWrapper<E>(new ArrayList<E>(pojo.getObservableSet()));
And I have some problems with it:
Will editing this in the table update the underlying set as expected?
Is it the 'right' way of doing this
So in short I need a style guide or best practice for converting between observable set and observable list because I expect to be doing this a lot when building a java fx GUI
Will editing this in the table update the underlying set as expected ?
No because, you are doing a copy of the set:
new ArrayList<E>(pojo.getObservableSet())
Is it the 'right' way of doing this ?
I think the right way is not doing that. Set are not List and vice versa. Both have specific contraints. For example, the lists are ordered and sets contains no duplicate elements.
Moreover, nor FXCollections neither Bindings provides this kind of stuff.
I would like the collection to remain as a set to enforce uniqueness
I guess you could write a custom ObservableList, for example the Parent::children have a similar behavior. It throws an IllegalArgumentException if a duplicate children is added. If you look at the source code, you will see that it is a VetoableListDecorator extension. You could write your own:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import com.sun.javafx.collections.VetoableListDecorator;
public class CustomObservableList<E> extends VetoableListDecorator<E> {
public CustomObservableList(ObservableList<E> decorated) {
super(decorated);
}
#Override
protected void onProposedChange(List<E> toBeAdded, int... indexes) {
for (E e : toBeAdded) {
if (contains(e)) {
throw new IllegalArgumentException("Duplicament element added");
}
}
}
}
class Test {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Set<Object> set = new HashSet<Object>();
set.add(o1);
CustomObservableList<Object> list = new CustomObservableList<Object>(FXCollections.observableArrayList(set));
list.add(o2);
list.add(o1); // throw Exception
}
}
Just in Case someone stumbles over this question looking for a one-way to convert an ObservableSet into an ObservableList... I post my solution. It doesn't support feeding back data to the set (which in my opinion wouldn't be nice since TableView doesn't have a concept of not being able to change a value) but supports updates of the set and preserves the (in this case) sorted order.
package de.fluxparticle.lab;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Collections;
import java.util.Random;
import java.util.TreeSet;
import static javafx.collections.FXCollections.observableSet;
/**
* Created by sreinck on 23.01.17.
*/
public class Set2List extends Application {
private final ObservableSet<Integer> setModel = observableSet(new TreeSet<Integer>());
#Override
public void start(Stage primaryStage) throws Exception {
TableView<Integer> tableView = new TableView<>();
addColumn(tableView, "Number");
ObservableList<Integer> list = convertSetToList(setModel);
tableView.setItems(list);
Random rnd = new Random();
scheduleTask(Duration.millis(1000), () -> setModel.add(rnd.nextInt(10)));
primaryStage.setScene(new Scene(tableView, 800, 600));
primaryStage.setTitle("Set2List");
primaryStage.show();
}
private static void scheduleTask(Duration interval, Runnable task) {
Timeline timeline = new Timeline(new KeyFrame(interval, event -> task.run()));
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
private static ObservableList<Integer> convertSetToList(ObservableSet<Integer> set) {
ObservableList<Integer> list = FXCollections.observableArrayList(set);
set.addListener((SetChangeListener<Integer>) change -> {
if (change.wasAdded()) {
Integer added = change.getElementAdded();
int idx = -Collections.binarySearch(list, added)-1;
list.add(idx, added);
} else {
Integer removed = change.getElementRemoved();
int idx = Collections.binarySearch(list, removed);
list.remove(idx);
}
});
return list;
}
private static void addColumn(TableView<Integer> tableView, String text) {
TableColumn<Integer, String> column = new TableColumn<>(text);
column.setCellValueFactory(param -> new SimpleStringProperty(param.getValue().toString()));
tableView.getColumns().add(column);
}
public static void main(String[] args) {
launch(args);
}
}
I'm trying to sort a hashmap's by sorting it's keys but it doesn't work.
The sorting criteria is given by the length of a list that is the hashmap's value.
See code below with some unit test.
Class:
package com.fabri.interpreter.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import com.fabri.interpreter.VerbExpr;
import com.fabri.interpreter.ObjectExpr;
public class Environment {
private HashMap<VerbExpr, List<ObjectExpr>> map = new HashMap<VerbExpr, List<ObjectExpr>>();
public List<ObjectExpr> eval(VerbExpr verb) {
return map.get(verb);
}
public void put(VerbExpr verb, ObjectExpr words) {
List<ObjectExpr> values;
if(map.get(verb) == null)
values = new ArrayList<ObjectExpr>();
else
values = map.get(verb);
values.add(words);
map.put(verb, values);
}
public HashMap<VerbExpr, List<ObjectExpr>> getMap() {
return map;
}
public void sort() {
List<VerbExpr> keys = new ArrayList<VerbExpr>(map.keySet());
Collections.sort(keys, new Comparator<VerbExpr>() {
#Override
public int compare(VerbExpr verb1, VerbExpr verb2) {
return map.get(verb1).size()-map.get(verb2).size();
}
});
HashMap<VerbExpr, List<ObjectExpr>> sortedMap = new HashMap<VerbExpr, List<ObjectExpr>>();
for(VerbExpr verb : keys) {
sortedMap.put(verb, map.get(verb));
}
map = sortedMap;
}
}
Testing class:
package com.fabri.interpreter.util;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.fabri.interpreter.ObjectExpr;
import com.fabri.interpreter.VerbExpr;
import com.fabri.interpreter.WordExpr;
public class TestEnvironment {
private Object[] verbExprs;
#Before
public void setUp() {
Environment env = new Environment();
List<WordExpr> words1 = new ArrayList<WordExpr>();
words1.add(new WordExpr("american"));
words1.add(new WordExpr("italian"));
env.put(new VerbExpr("was"), new ObjectExpr(words1));
List<WordExpr> words2 = new ArrayList<WordExpr>();
words2.add(new WordExpr("zero"));
words2.add(new WordExpr("one"));
words2.add(new WordExpr("two"));
env.put(new VerbExpr("is"), new ObjectExpr(words2));
env.sort();
verbExprs = env.getMap().keySet().toArray();
}
#Test
public void testEnvironment() {
assertTrue(((VerbExpr)verbExprs[0]).equals("is"));
assertTrue(((VerbExpr)verbExprs[1]).equals("was"));
}
}
Plain hashmaps are inherently unordered. You can't sort them, or assume anything about the order in which the entries are retrieved when iterating over them. Options:
Use a TreeMap if you want to sort by key.
Use a LinkedHashMap if you want to preserve insertion order (which is what your sort method looks like it assumes)
Create a list of key/value pairs and sort that instead.
As jon said I would suggest keeping an ordered list of keys, and using that to access the inherently unordered hash map.