how could I test Lists in JUnit5? - java

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.

Related

How to mock CloseableIterator in a test?

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.

Mock spliterator for Iterable implementation when called several times?

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

Java 8 Lambda stack bleed

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class StackBleed {
public static int check(String s) {
if (s.equals("lambda")) {
throw new IllegalArgumentException();
}
return s.length();
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
// List lengths = new ArrayList();
List<String> argList = Arrays.asList(args);
Stream lengths2 = argList.stream().map((String name) -> check(name));
}
}
So I was checking out this article http://www.takipiblog.com/2014/03/25/the-dark-side-of-lambda-expressions-in-java-8/ and wrote similar class, but JDK 8 approach didn't yield expected exception. I was wondering if they have changed something in JDK 8u5?
You're only calling a non-terminal operation on the stream. So your code doesn't consume the data from the stream. All it does is saying: "when a terminal operation will be called, you'll have to map the strings using the check() method".
Use
List<Integer> transformed =
argList.stream().map((String name) -> check(name)).collect(Collectors.toList());
for example, and then the call to collect(), which is a terminal operation, will trigger the iteration on the stream elements and the transformation of its elements.

Java - List sorting doesn't work

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.

Tricky ClassCastException in using the Plastic framework for code generation

What's it all about
Hi everyone - I was looking at a presentation called "Have your cake and eat it too: Meta-programming in Java"
The presenter was Howard M. Lewis Ship, (one?) of the authors of Tapestry - and in making that, a subproject called "plastic" was made to leverage ASM to alter the bytecode.
I won't pretend to be an expert, but the end-result should be that I can write code such that annotated classes, methods and fields can be used to generate further java code thus cutting down on boilerplate code.
My Question
The code below is a complete example to demonstrate my problem. The test example should modify the EqualsDemo class such that it contains implementations of equals() and hashCode().
When running it I get an error which basically states that I can't cast an object of type 'com.example.plastic.transformed.EqualsDemo' to 'com.example.plastic.transformed.EqualsDemo' (yes, the same class).
The presenter just mentioned that these errors were annoying without alluding to where they stem from - my searching so far indicates that they pertain to different class loaders.
I have, however, been completely unable to fix the problem, hence my question here(!)
com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
at MainClass.main(MainClass.java:28)
So what do I need to do ? Replace class loaders ? (if so, how?) or is there some part of Plastic I don't get ? Some method for generating proxy objects or similar that I need to use for things to go smoothly ?
PS!
The examples I've found so far all use what I think is Groovy in the final use of the annotated instances.
Hopefully someone are more competent than I :)
Links:
Tapestry Homepage (plastic is contained as a jar in the download): http://tapestry.apache.org/
Main.java
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.internal.plastic.StandardDelegate;
import org.apache.tapestry5.plastic.ClassInstantiator;
import org.apache.tapestry5.plastic.PlasticManager;
import com.example.plastic.transformer.EqualsHashCodeTransformer;
import com.example.plastic.transformed.EqualsDemo;
public class MainClass {
public static void main(String[] args) {
List<String> pList = new ArrayList<String>();
pList.add("com.example.plastic.transformed");
PlasticManager pm = PlasticManager
.withContextClassLoader()
.delegate( new StandardDelegate(new EqualsHashCodeTransformer()) )
.packages(pList)
.create();
final String EQUALSDEMO = "com.example.plastic.transformed.EqualsDemo";
ClassInstantiator<EqualsDemo> i = pm.getClassInstantiator(EQUALSDEMO);
i.newInstance().hashCode();
/*
com.example.plastic.transformed.EqualsDemo cannot be cast to com.example.plastic.transformed.EqualsDemo
at MainClass.main(MainClass.java:28)
*/
}
}
ImplementEqualsHashCode.java
package com.example.plastic.annotations;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.*;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ImplementEqualsHashCode {
}
EqualsHashCodeTransformer.java
package com.example.plastic.transformer;
import java.util.ArrayList;
import java.util.List;
import org.apache.tapestry5.plastic.FieldHandle;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodDescription;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.plastic.PlasticClass;
import org.apache.tapestry5.plastic.PlasticClassTransformer;
import org.apache.tapestry5.plastic.PlasticField;
import com.example.plastic.annotations.*;
public class EqualsHashCodeTransformer implements PlasticClassTransformer {
private MethodDescription EQUALS = new MethodDescription("boolean", "equals", "java.lang.Object");
private MethodDescription HASHCODE = new MethodDescription("int", "hashCode");
private static final int PRIME = 37;
public void transform(PlasticClass plasticClass){
//check that the class is annotated
if(!plasticClass.hasAnnotation(ImplementEqualsHashCode.class)) {
return;
}
List<PlasticField> fields = plasticClass.getAllFields();
final List<FieldHandle> handles = new ArrayList<FieldHandle>();
for(PlasticField field : fields){
handles.add(field.getHandle());
}
//HashCode method introduction :)
plasticClass.introduceMethod(HASHCODE).addAdvice(new MethodAdvice() {
public void advise(MethodInvocation invocation){
Object instance = invocation.getInstance();
int result = 1;
for(FieldHandle handle : handles){
Object fieldValue = handle.get(instance);
if(fieldValue != null)
result = (result * PRIME) + fieldValue.hashCode();
}
invocation.setReturnValue(result);
//Don't proceed to the empty introduced method
}
});
plasticClass.introduceMethod(EQUALS).addAdvice(new MethodAdvice() {
public void advise(MethodInvocation invocation) {
Object thisInstance = invocation.getInstance();
Object otherInstance = invocation.getParameter(0);
invocation.setReturnValue(isEqual(thisInstance, otherInstance));
//Don't proceed to the empty introduced method
}
private boolean isEqual(Object thisInstance, Object otherInstance) {
if(thisInstance == otherInstance)
return true;
if(otherInstance == null)
return false;
if(!(thisInstance.getClass() == otherInstance.getClass()))
return false;
for(FieldHandle handle : handles){
Object thisValue = handle.get(thisInstance);
Object otherValue = handle.get(otherInstance);
if(!(thisValue == otherValue || thisValue.equals(otherValue)))
return false;
}
return true;
}
});
}
}
EqualsDemo.java
package com.example.plastic.transformed;
import com.example.plastic.annotations.ImplementEqualsHashCode;
#ImplementEqualsHashCode
public class EqualsDemo {
private int intValue;
private String stringValue;
public int getIntValue(){
return intValue;
}
public void setIntValue(int intValue){
this.intValue = intValue;
}
public String getStringValue(){
return stringValue;
}
public void setStringValue(String stringValue){
this.stringValue = stringValue;
}
}
you don't want to add the packages to the plastic manager -- it uses a different classloader and will load those classes, making two copies of the classes in those packages (one in the parent classloader and one in the plastic classloader) which will give you the ClassCastException you are seeing when the framework tries to cast to your class. Try this instead:
import org.apache.tapestry5.internal.plastic.StandardDelegate;
import org.apache.tapestry5.plastic.ClassInstantiator;
import org.apache.tapestry5.plastic.PlasticManager;
public class MainClass {
public static void main(String[] args) {
PlasticManager pm = PlasticManager
.withContextClassLoader()
.delegate(new StandardDelegate())
.create();
ClassInstantiator<EqualsDemo> ci = pm.createClass(EqualsDemo.class, new EqualsHashCodeTransformer());
System.out.println(ci.newInstance().hashCode());
}
}
I guess instead of
PlasticManager.withContextClassLoader()...
using the following should fix your problem:
PlasticManager.withClassLoader(getClass().getClassLoader())...

Categories

Resources