Easiest way to test if two collections have the same contents? - java

Very often when writing tests I've to check if two collections have the same contents and sometimes even if they have the same order. So I endlessly end up doing the same thing:
assertEquals(collection1.size(), collection2.size());
for (ItemType item : collection1){
if (!collection2.contains(item)) fail(); //This depends on the collection
}
//some more code is required to test ordering
Do you know of a good way to end this torment using some standard library?

Better use equals() method, because if you use containsAll then for two lists that have same elements can be equal although there elements are in different order. So using containsAll is not good way to compare List
Here is a demo
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class TwoButtons {
public static void main(String[] args){
Collection<Integer> c1 = new ArrayList<>(Arrays.asList(1,2,3));
Collection<Integer> c2 = new ArrayList<>(Arrays.asList(3,2,1));
System.out.println("equals " + c1.equals(c2));
System.out.println("containsAll " + c1.containsAll(c2));
}
}
output
equals false
containsAll true

You can do use this as a condition
collection1.size()==collection2.size() && collection1.containsAll(collection2)
Here you are checking that both the collections are of same size as well as the have all the elements.
As per the comments by joachim-isaksson
you can do this but will not be efficient.
collection1.containsAll(collection2) && collection2.containsAll(collection1)
You should use
ReflextionAsserter

Related

Runtime problems in TestNG asserts

I have a TestNG assertion which may occasionally fail due to the state of the object asserted (ArrayList element), and when it does, I would like to display this state.
I created an example which will fail in runtime, to illustrate the concept
import static org.testng.Assert.assertEquals;
#Test
public void sandbox() {
ArrayList<Integer> arr = new ArrayList<>();
assertEquals(arr.size(), 0, "The problem is: " + arr.get(0).toString());
}
I expected the assertion to pass and it will, when I remove the third argument (message). I see it's not a TestNG issue as execution is not stepping in the statement, but rather fails directly at this step with
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.get(ArrayList.java:433)
What's the best approach here? I am thinking of a method which will take care of the exception, but perhaps there are better known ways.
Thanks
Below should work:
import static org.testng.Assert.assertEquals;
#Test
public void sandbox() {
ArrayList<Integer> arr = new ArrayList<>();
assertEquals(arr.size(), 0, "The problem is: " + arr);
}
This is more practical as well, because if list is not empty it will print all values from the list instead of the first.
There is no other pretty way of doing it since all your arguments are passed at once to the callee, i.e. your method, so that the arr.get(0) must have been evaluated.
You may simply call for the next() element only if there is one using the Iterator over your collection:
#Test
public void sandbox() {
ArrayList<Integer> arr = new ArrayList<>();
Iterator<Integer> it = arr.iterator();
assertEquals(arr.size(), 0, "The problem is: " + (it.hasNext() ? iterator.next() : ""));
}

List Iterator - Not working

I've below code:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class IteratorExample {
public static void main(String[] args) {
List<String> listnames = new ArrayList<String>();
listnames.add("Tom");
listnames.add("Finn");
listnames.add("Harry");
ListIterator<String> iteratorNames = listnames.listIterator();
while (iteratorNames.hasNext()) {
System.out.println(iteratorNames);
}
}
}
When I execute, I am getting strange output like below(which differs everytime when I run the program):
java.util.ArrayList$ListItr#a200d0c
java.util.ArrayList$ListItr#a200d0c
java.util.ArrayList$ListItr#a200d0c
java.util.ArrayList$ListItr#a200d0c
java.util.ArrayList$ListItr#a200d0c
Also the program is running infinitely.
Why it is not printing the list values?
You're looking at the iterator itself.
use
iteratorNames.next()
to get the next item.
Change the below line:
System.out.println(iteratorNames);
To:
System.out.println(iteratorNames.next());
I am getting strange output like below
It's just printing the memory adress of the object using the toString() default implementation of the object class.
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
Also the program is running infinitely.
As other answers stated, you have to use next()(i.e System.out.println(iteratorNames.next());) for two reasons :
It allows you to get the element in the list while iterating it
It advances the cursor position of the iterator
That's why your program runs indefinitely, because the cursor is still on the first position on your list, so hasNext() will always returns true.
Using next() the program looks like :
while (iteratorNames.hasNext()) {
String element = iteratorNames.next(); //now you can do what you want with this element
System.out.println(element);
}
Others have already mentioned how you use an Iterator--by querying it to see if it has a next value and then grabbing it via next() if it does.
However, you have the option of avoiding those low-level details altogether through a nice syntactic abstraction:
for (String name : listnames) {
System.out.println(name);
}
Give it a shot. In my opinion, it was one of the most helpful features of Java 5. Here is more information on it.
In your program System.out.println(iteratorNames); it will call toString() of Object class that will returns a string representation of the object (see implementation on toString())
public String toString() {
return getClass().getName() + "#" + Integer.toHexString(hashCode());
}
That is why you are getting output like java.util.ArrayList$ListItr#a200d0c.
call next() It will returns the next element in the list. as follows:
System.out.println(iteratorNames.next());
use,
System.out.println((String)iteratorNames.next());

How do I assert equality on two classes without an equals method?

Say I have a class with no equals() method, to which do not have the source. I want to assert equality on two instances of that class.
I can do multiple asserts:
assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...
I don't like this solution because I don't get the full equality picture if an early assert fails.
I can manually compare on my own and track the result:
String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);
This gives me the full equality picture, but is clunky (and I haven't even accounted for possible null problems). A third option is to use Comparator, but compareTo() will not tell me which fields failed equality.
Is there a better practice to get what I want from the object, without subclassing and overridding equals (ugh)?
There is many correct answers here, but I would like to add my version too. This is based on Assertj.
import static org.assertj.core.api.Assertions.assertThat;
public class TestClass {
public void test() {
// do the actual test
assertThat(actualObject)
.isEqualToComparingFieldByFieldRecursively(expectedObject);
}
}
UPDATE: In assertj v3.13.2 this method is deprecated as pointed out by Woodz in comment. Current recommendation is
public class TestClass {
public void test() {
// do the actual test
assertThat(actualObject)
.usingRecursiveComparison()
.isEqualTo(expectedObject);
}
}
Mockito offers a reflection-matcher:
For latest version of Mockito use:
Assert.assertTrue(new ReflectionEquals(expected, excludeFields).matches(actual));
For older versions use:
Assert.assertThat(actual, new ReflectionEquals(expected, excludeFields));
I generally implement this usecase using org.apache.commons.lang3.builder.EqualsBuilder
Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));
I know it's a bit old, but I hope it helps.
I run into the same problem that you, so, after investigation, I found few similar questions than this one, and, after finding the solution, I'm answering the same in those, since I thought it could to help others.
The most voted answer (not the one picked by the author) of this similar question, is the most suitable solution for you.
Basically, it consist on using the library called Unitils.
This is the use:
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
Which will pass even if the class User doesn't implement equals(). You can see more examples and a really cool assert called assertLenientEquals in their tutorial.
If you're using hamcrest for your asserts (assertThat) and don't want to pull in additional test libs, then you can use SamePropertyValuesAs.samePropertyValuesAs to assert items that don't have an overridden equals method.
The upside is that you don't have to pull in yet another test framework and it'll give a useful error when the assert fails (expected: field=<value> but was field=<something else>) instead of expected: true but was false if you use something like EqualsBuilder.reflectionEquals().
The downside is that it is a shallow compare and there's no option for excluding fields (like there is in EqualsBuilder), so you'll have to work around nested objects (e.g. remove them and compare them independently).
Best Case:
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
assertThat(actual, is(samePropertyValuesAs(expected)));
Ugly Case:
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
SomeClass expected = buildExpected();
SomeClass actual = sut.doSomething();
assertThat(actual.getSubObject(), is(samePropertyValuesAs(expected.getSubObject())));
expected.setSubObject(null);
actual.setSubObject(null);
assertThat(actual, is(samePropertyValuesAs(expected)));
So, pick your poison. Additional framework (e.g. Unitils), unhelpful error (e.g. EqualsBuilder), or shallow compare (hamcrest).
You can use Apache commons lang ReflectionToStringBuilder
You can either specify the attributes you want to test one by one, or better, exclude those you don't want:
String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE)
.setExcludeFieldNames(new String[] { "foo", "bar" }).toString()
You then compare the two strings as normal. For the point about reflection being slow, I assume this is only for testing, so shouldn't be so important.
Since this question is old, I will suggest another modern approach using JUnit 5.
I don't like this solution because I don't get the full equality picture if an early assert fails.
With JUnit 5, there is a method called Assertions.assertAll() which will allow you to group all assertions in your test together and it will execute each one and output any failed assertions at the end. This means that any assertions that fail first will not stop the execution of latter assertions.
assertAll("Test obj1 with obj2 equality",
() -> assertEquals(obj1.getFieldA(), obj2.getFieldA()),
() -> assertEquals(obj1.getFieldB(), obj2.getFieldB()),
() -> assertEquals(obj1.getFieldC(), obj2.getFieldC()));
The library Hamcrest 1.3 Utility Matchers has a special matcher that uses reflection instead of equals.
assertThat(obj1, reflectEquals(obj2));
Some of the reflection compare methods are shallow
Another option is to convert the object to a json and compare the strings.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public static String getJsonString(Object obj) {
try {
ObjectMapper objectMapper = new ObjectMapper();
return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (JsonProcessingException e) {
LOGGER.error("Error parsing log entry", e);
return null;
}
}
...
assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject))
AssertJ assertions can be used to compare the values without #equals method properly overridden, e.g.:
import static org.assertj.core.api.Assertions.assertThat;
// ...
assertThat(actual)
.usingRecursiveComparison()
.isEqualTo(expected);
Using Shazamcrest, you can do:
assertThat(obj1, sameBeanAs(obj2));
Compare field-by-field:
assertNotNull("Object 1 is null", obj1);
assertNotNull("Object 2 is null", obj2);
assertEquals("Field A differs", obj1.getFieldA(), obj2.getFieldA());
assertEquals("Field B differs", obj1.getFieldB(), obj2.getFieldB());
...
assertEquals("Objects are not equal.", obj1, obj2);
You can use reflection to "automate" the full equality testing. you can implement the equality "tracking" code you wrote for a single field, then use reflection to run that test on all fields in the object.
In case you just need flat fields comparison you can use AssertJ
Assertions.assertThat(actual)).isEqualToComparingFieldByField(expected);
This is a generic compare method , that compares two objects of a same class for its values of it fields(keep in mind those are accessible by get method)
public static <T> void compare(T a, T b) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
AssertionError error = null;
Class A = a.getClass();
Class B = a.getClass();
for (Method mA : A.getDeclaredMethods()) {
if (mA.getName().startsWith("get")) {
Method mB = B.getMethod(mA.getName(),null );
try {
Assert.assertEquals("Not Matched = ",mA.invoke(a),mB.invoke(b));
}catch (AssertionError e){
if(error==null){
error = new AssertionError(e);
}
else {
error.addSuppressed(e);
}
}
}
}
if(error!=null){
throw error ;
}
}
I stumbled on a very similar case.
I wanted to compare on a test that an object had the same attribute values as another one, but methods like is(), refEq(), etc wouldn't work for reasons like my object having a null value in its id attribute.
So this was the solution I found (well, a coworker found):
import static org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare;
assertThat(reflectionCompare(expectedObject, actualObject, new String[]{"fields","to","be","excluded"}), is(0));
If the value obtained from reflectionCompare is 0, it means they are equal. If it is -1 or 1, they differ on some attribute.
In common case with AssertJ you can create custom comparator strategy:
assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam)
assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);
Using a custom comparison strategy in assertions
AssertJ examples
I had the exact same conundrum when unit testing an Android app, and the easiest solution I came up with was simply to use Gson to convert my actual and expected value objects into json and compare them as strings.
String actual = new Gson().toJson( myObj.getValues() );
String expected = new Gson().toJson( new MyValues(true,1) );
assertEquals(expected, actual);
The advantages of this over manually comparing field-by-field is that you compare all your fields, so even if you later on add a new field to your class it will get automatically tested, as compared to if you were using a bunch of assertEquals() on all the fields, which would then need to be updated if you add more fields to your class.
jUnit also displays the strings for you so you can directly see where they differ. Not sure how reliable the field ordering by Gson is though, that could be a potential problem.
I tried all the answers and nothing really worked for me.
So I've created my own method that compares simple java objects without going deep into nested structures...
Method returns null if all fields match or string containing mismatch details.
Only properties that have a getter method are compared.
How to use
assertNull(TestUtils.diff(obj1,obj2,ignore_field1, ignore_field2));
Sample output if there is a mismatch
Output shows property names and respective values of compared objects
alert_id(1:2), city(Moscow:London)
Code (Java 8 and above):
public static String diff(Object x1, Object x2, String ... ignored) throws Exception{
final StringBuilder response = new StringBuilder();
for (Method m:Arrays.stream(x1.getClass().getMethods()).filter(m->m.getName().startsWith("get")
&& m.getParameterCount()==0).collect(toList())){
final String field = m.getName().substring(3).toLowerCase();
if (Arrays.stream(ignored).map(x->x.toLowerCase()).noneMatch(ignoredField->ignoredField.equals(field))){
Object v1 = m.invoke(x1);
Object v2 = m.invoke(x2);
if ( (v1!=null && !v1.equals(v2)) || (v2!=null && !v2.equals(v1))){
response.append(field).append("(").append(v1).append(":").append(v2).append(")").append(", ");
}
}
}
return response.length()==0?null:response.substring(0,response.length()-2);
}
For Unit testing I just serialize the object to a JSON string and compare it.
For example with Gson:
import com.google.gson.GsonBuilder
import junit.framework.TestCase.assertEquals
class AssertEqualContent {
companion object {
val gson = GsonBuilder().create()
fun assertEqualContent(message: String?, expected: Any?, actual: Any?) {
assertEquals(message, gson.toJson(expected), gson.toJson(actual))
}
}
}
As the expected and actual object is supposed to be of the same type the field order will be the same.
Pros:
You will get a nice string comparison highligting exactly where the difference is.
No extra libraries (provided that you have a JSON library already)
Cons:
Objects of different types might produce the same JSON (but if they do, you might consider why do you have different classes for the same data.... and how they could end up being compared in a testing method :-)
Can you put the comparision code you posted into some static utility method?
public static String findDifference(Type obj1, Type obj2) {
String difference = "";
if (obj1.getFieldA() == null && obj2.getFieldA() != null
|| !obj1.getFieldA().equals(obj2.getFieldA())) {
difference += "Difference at field A:" + "obj1 - "
+ obj1.getFieldA() + ", obj2 - " + obj2.getFieldA();
}
if (obj1.getFieldB() == null && obj2.getFieldB() != null
|| !obj1.getFieldB().equals(obj2.getFieldB())) {
difference += "Difference at field B:" + "obj1 - "
+ obj1.getFieldB() + ", obj2 - " + obj2.getFieldB();
// (...)
}
return difference;
}
Than you can use this method in JUnit like this:
assertEquals("Objects aren't equal", "", findDifferences(obj1, obj));
which isn't clunky and gives you full information about differences, if they exist (through not exactly in normal form of assertEqual but you get all the info so it should be good).
From your comments to other answers, I don't understand what you want.
Just for the sake of discussion, lets say that the the class did override the equals method.
So your UT will look something like:
SomeType expected = // bla
SomeType actual = // bli
Assert.assertEquals(expected, actual).
And you are done. Moreover, you can not get the "full equality picture" if the assertion fails.
From what I understand, you are saying that even if the type did override equals, you would not be interested in it, since you want to get the "full equality picture". So there is no point in extending and overriding equals either.
So you have to options: either compare property by property, using reflection or hard-coded checks, I would suggest the latter. Or: compare human readable representations of these objects.
For example, you can create a helper class that serializes the type you wish tocompare to an XML document and than compare the resulting XML! in this case, you can visually see what exactly is equal and what is not.
This approach will give you the opportunity to look at the full picture but it is also relatively cumbersome (and a little error prone at first).
You can override the equals method of the class like:
#Override
public int hashCode() {
int hash = 0;
hash += (app != null ? app.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
HubRule other = (HubRule) object;
if (this.app.equals(other.app)) {
boolean operatorHubList = false;
if (other.operator != null ? this.operator != null ? this.operator
.equals(other.operator) : false : true) {
operatorHubList = true;
}
if (operatorHubList) {
return true;
} else {
return false;
}
} else {
return false;
}
}
Well, if you want to compare two object from a class you must implement in some way the equals and the hash code method

values in queue Java

If I have a queue with strings as values How would I print all the values I was using:
System.out.println(queue.elements().toString().);
But it prints java objects...?
Do I have to use a loop to print values of queue?
Yes, you will need to use a loop however the loop can be simple like.
for(String s : queue) {
System.out.println(s.toString());
}
Actually, as long as it implements Iterable you should be able to do this type of foreach loop.
I don't understand your question. You say you want to print strings, and then after some code that doesn't compile, you say, "But it prints java objects...?" Strings are Java objects.
import java.util.*;
public class Foo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>(); // create queue
for (String name : args) queue.add(name); // add strings to queue
System.out.println(queue); // print entire queue at once
for (String s : queue) System.out.println(s); // print each separately
}
}
Run with e.g., java Foo apple banana pear. The output is:
[apple, banana, pear]
apple
banana
pear
If the problem is that you are getting output like this:
[MyObject#498b5a73, MyObject#5bdf59bd, MyObject#247cb66a]
MyObject#498b5a73
MyObject#5bdf59bd
MyObject#247cb66a
Then your Queue does not actually hold Strings, and you have forgotten to override the toString() method in your class:
#Override public String toString() {
return firstName + " " + lastName + ", " + quest + ", " + favoriteColor;
}
There is no need to use a loop, unless you don't [like, this, output, format].
The answer depends on the type of queue.
If it is a Queue or Deque, then your code won't compile, because these interfaces don't define an elements() method. This applies to most of the classes that implement Collection.
If it is a Vector, then the elements() returns an Enumeration, and you have to use a loop to pull the values from it.
My advice would be:
Stop using Vector ... unless you have no choice. Vector is a legacy class. Use one of the implementations of Queue or Deque instead.
Whether or not you use Vector, use queue.toString() rather than queue.elements().toString() to render the queue contents as a String. The toString() method is defined as rendering the elements of the collection for all of the standard collection classes.
Try this:
Enumeration e = queue.elements();
while ( e.hasMoreElements() )
System.out.println( e.nextElement() );
Look at Joiner in guava.
System.out.println(Joiner.on("\n").join(queue.elememts()));
try this
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
Queue myQueue = new LinkedList();
myQueue.add("A");
myQueue.add("B");
myQueue.add("C");
myQueue.add("D");
List<String> myList = new ArrayList<String>(myQueue);
for (Object theFruit : myList)
System.out.println(theFruit);
}
}
Java 1.8+ solution:
queue.forEach(System.out::println);

Java Collections containsAll Weired Behavior

I have following code , where I am using superList and subList , I want to check that subList is actually a subList of superList.
My objects do not implement hashCode or equals methods. I have created the similar situation in the test. When I run the test then the result show very big performance difference between results from JDK collection and common collections.After Running the test I am getting following output.
Time Lapsed with Java Collection API 8953 MilliSeconds & Result is true
Time Lapsed with Commons Collection API 78 MilliSeconds & Result is true
My question is why is java collection , so slow in processing the containsAll operation. Am I doing something wrong there? I have no control over collection Types I am getting that from legacy code. I know if I use HashSet for superList then I would get big performance gains using JDK containsAll operation, but unfortunately that is not possible for me.
package com.mycompany.tests;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Before;
import org.junit.Test;
public class CollectionComparison_UnitTest {
private Collection<MyClass> superList = new ArrayList<MyClass>();
private Collection<MyClass> subList = new HashSet<MyClass>(50000);
#Before
public void setUp() throws Exception {
for (int i = 0; i < 50000; i++) {
MyClass myClass = new MyClass(i + "A String");
superList.add(myClass);
subList.add(myClass);
}
#Test
public void testIt() {
long startTime = System.currentTimeMillis();
boolean isSubList = superList.containsAll(subList);
System.out.println("Time Lapsed with Java Collection API "
+ (System.currentTimeMillis() - startTime)
+ " MilliSeconds & Result is " + isSubList);
startTime = System.currentTimeMillis();
isSubList = CollectionUtils.isSubCollection(subList, superList);
System.out.println("Time Lapsed with Commons Collection API "
+ (System.currentTimeMillis() - startTime)
+ " MilliSeconds & Result is " + isSubList);
}
}
class MyClass {
String myString;
MyClass(String myString) {
this.myString = myString;
}
String getMyString() {
return myString;
}
}
Different algorithms:
ArrayList.containsAll() offers O(N*N), while CollectionUtils.isSubCollection() offers O(N+N+N).
ArrayList.containsAll is inherited from AbstractCollection.containsAll and is a simple loop checking all elements in row. Each step is a slow linear search. I don't know how CollectionUtils works, but it's not hard to do it much faster then using the simple loop. Converting the second List to a HashSet is a sure win. Sorting both lists and going through them in parallel could be even better.
EDIT:
The CollectionUtils source code makes it clear. They're converting both collections to "cardinality maps", which is a simple and general way for many operations. In some cases it may not be a good idea, e.g., when the first list is empty or very short, you in fact loose time. In you case it's a huge win in comparison to AbstractCollection.containsAll, but you could do even better.
Addendum years later
The OP wrote
I know if I use HashSet for superList then I would get big performance gains using JDK containsAll operation, but unfortunately that is not possible for me.
and that's wrong. Classes without hashCode and equals inherit them from Object and can be used with a HashSet and everything works perfectly. Except for that each object is unique, which may be unintended and surprising, but the OP's test superList.containsAll(subList) does exactly the same thing.
So the quick solutions would be
new HashSet<>(superList).containsAll(subList)
You should at least try the tests in the opposite order. Your results may very well just show that the JIT compiler is doing its job well :-)

Categories