Convert logger.debug("message: " + text) to logger.debug(message: {}", text) - java

I am trying to find the best way to address the issue of redundant string concatenation caused by using code of the following form:
logger.debug("Entering loop, arg is: " + arg) // #1
In most cases the logger.level is higher than debug and the arg.toString() and the string concatenation are a waste that user up cpu cycles and briefly use up memory.
Before the introduction of varargs the recommended approach was to test the logger level first:
if (logger.isDebugEnabled())
logger.debug("Entering loop, arg is: " + arg); // #2
But now the preferred form is
logger.debug("Entering loop, arg is: {}", arg); // #3
It is not very difficult to prefix each logger.debug with if (logger.isDebugEnabled()) (and its equivalent for the other methods) in a script, but I am trying to find the best way to convert the first form to the third.
Any suggestions? The challenge is to insert the correct number brace pairs {} in the format string. I wish logback would append the remaining arguments not covered by the placeholder at the end but I cannot find a reference that it does that.
As an alternative, I am thinking to write a class Concatenator as pasted at end and convert the first form to
logger.debug(new Concatenator("Entering loop, arg is: ", arg)); // #4
The Concatenator class delays the call to arg.toString() and string concatenation until the logger calls toString(), thereby avoiding both if the logger is at a higher filter level. It does add the overhead of creating an Object[] and a Concatenator but that should be cheaper than the alternative.
Questions:
I think this conversion (#1->#4 -- replace + with , and enclose in new Contatenator(...)) is much easier. Is there something I am missing?
Am I correct that #4 is much better than #1?
public class Concatenator {
final Object[] input;
String output;
public Concatenator(Object... input) {
this.input = input;
}
public String toString() {
if (output == null) {
StringBuffer b = new StringBuffer();
for (Object s : input) b.append(s.toString());
output = b.toString();
}
return output;
}
public static void main(String args[]) {
new Concatenator("a", "b", new X());
System.out.println(new Concatenator("c", "d", new X()));
}
}
class X {
public String toString() {
System.out.println("X.toString");
return super.toString();
}
}

Unfortunately your approach isn't going to change anything. In fact, it introduces an additional object instantiation/allocation (your Concatenator). You're also using StringBuffer which introduces synchronization overhead you don't need.
The problem is the method signature for SLF4J's Logger.debug() calls. The first argument is always a String. This means you're going to have to call:
logger.debug(new Concatenator("Entering loop, arg is: ", arg).toString());
which means ... you're doing exactly the same thing as Java is going to do, but with more overhead.
The Java compiler handles the String concatenation operator (+) by creating a StringBuilder and doing exactly what you're doing in your Concatenator class on toString().
logger.debug("Entering loop, arg is: " + arg);
becomes:
logger.debug(new StringBuilder()
.append("Entering loop, arg is: ")
.append(arg).toString());
(If you use javap to look at the generated bytecode, you'll see that's the case.)
So, your current approach is going to be more expensive than what you have now.
Edit: So, the way you could make this work is by doing ...
logger.debug("{}", new Concatenator("Entering loop, arg is: ", arg));
This way your Concatenator is passed as an Object and its toString() not called unless the logger needs to. Also, replace the StringBuffer in your class with StringBuilder.
And if I didn't answer your question directly ... is this better than the original? Probably; The string concatenation isn't occurring unless it needs to. You are, however, introducing an object instantiation/allocation. The only real way to see the differences would be to profile it / write a benchmark.

Related

How to automatically print all parameter values of Java function at runtime

I want to print automatically all the parameter values of my functions at runtime.
Just imagin that I have the following two methods:
public void doAction(String firstParam, String SecondParam) {
Util.printAllParameter(this);
}
public void doAction(String firstParam) {
Util.printAllParameter(this);
}
If I call to this functions:
doAction("a", "b"); --> Desired result: Print "a, b"
doAction("a"); --> Desired result: Print "a"
I don't want something like this (This is not reusable, it is static):
System.out.println(firstParam + "," + SecondParam);
I need a reusable method that I can use in different functions with different number of parameter. I want to call a function like "Util.printAllParameter()" and then print all the parameters.
Thanks in advance.
To do it generally would require rewriting the bytecode (probably with a Java Agent, or library using it) or the source code.
The way do it without hacking the code is to use an interface and a Proxy. Interfaces are often suggested, but Java gets in the way with its old fashioned, super verbose syntax.
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
// Gratuitous use of new fangled record feature and streams.
record TraceInvocation(PrintStream out) {
public <T> T trace(Class<T> type, T target) {
Objects.requireNonNull(target);
return type.cast(Proxy.newProxyInstance(
type.getClassLoader(),
new Class<?>[] { type },
(proxy, method, args) -> {
// Apparently args can be null. Ffs.
out.println(
(target==null ? type.getSimpleName() : escape(target))+
"."+method.getName()+
// There's probably a better way without {}.
"("+(args==null ? "" : String.join(", ",
Stream.of(args)
.map(TraceInvocation::escape)
.toArray(String[]::new)
))+")"
);
return method.invoke(target, args);
}
));
}
// Don't even think about allowing log injection.
// (Okay, weird syntax.)
private static String escape(Object object) {
// I am not a fan of streams.
int[] escaped = String.valueOf(object).codePoints().flatMap(cp ->
(cp == '\\' || cp == '.' || cp == ',') ?
IntStream.of('\\', cp) :
(' ' <= cp && cp <= '~' ) ?
IntStream.of(cp) :
("\\"+/*(int)*/cp+"\\").codePoints()
).toArray();
return new String(escaped, 0, escaped.length);
}
}
Use as:
CharSequence cs = new TraceInvocation(System.err)
.trace(CharSequence.class, "Scunthorpe");
cs.subSequence(4, 10).length(); // No log for length
cs.charAt(2);
cs.length();
Possible variation include filtering which methods to display, logging return values/exceptions, alternative to toString and tracing returned values.
I found this approach really useful when dealing with sending and receiving a stream in a proprietary format.
You can write a simple utility method that like:
public void trace(String methodName, Object... args) {
that then inspects all the arguments and prints a nice string for you.
But you have that explicit passing of the method name as string, which can't be avoided. Yes, theoretically, one call create a stack trace by throwing/catching an exception, and deduce the method name, but that is a real performance killer, which you absolutely can't do for ordinary tracing that is supposed to happen millions of time per day.
So: there are no "built-in" mechanisms in the Java language to get you there. No "macros" or some sort of "templating" to just "pull" such behavior into your production code without doing it explicitly.
But there is the concept of "Aspect Oriented Programming". Meaning: you have another tool that modifies your production code, for example to automatically add such trace statements, as a part of the build/compile process.
Certain technology stacks, for example spring might come with such technology.
If you are not using such frameworks, you would have to "add" something like that to your setup.

Translating a string-representation of a function's parameter list to actual parameters, for a reflective call

UPDATE: After getting an unexpected-in-a-good-way answer, I've added some context to the bottom of this question, stating exactly how I'll be using these string-function-calls.
I need to translate a string such as
my.package.ClassName#functionName(1, "a string value", true)
into a reflective call to that function. Getting the package, class, and function name is not a problem. I have started rolling my own solution for parsing the parameter list, and determining the type of each and returning an appropriate object.
(I'm limiting the universe of types to the eight primitives, plus string. null would be considered a string, and commas and double-quotes must be strictly escaped with some simple marker, such as __DBL_QT__, to avoid complications with unescaping and splitting on the comma.)
I am not asking how to do this via string-parsing, as I understand how. It's just a lot of work and I'm hoping there's a solution already out there. Unfortunately it's such generic terminology, I'm getting nowhere with searching.
I understand asking for an external existing library is off topic for SO. I'm just hoping to get some feedback before it's shutdown, or even a suggestion on better search terms. Or perhaps, there is a completely different approach that might be suggested...
Thank you.
Context:
Each function call is found within a function's JavaDoc block, and represents a piece of example code--either its source code or its System.out output--which will be displayed in that spot.
The parameters are for customizing its display, such as
indentation,
eliminating irrelevant parts (like the license-block), and
for JavaDoc-linking the most important functions.
This customization is mostly for the source-code presentation, but may also be applied to its output.
(The first parameter is always an Appendable, which will do the actual outputting.)
The user needs to be be able to call any function, which in many cases will be a private-static function located directly below the JavaDoc-ed function itself.
The application I'm writing will read in the source-code file (the one containing the JavaDoc blocks, in which these string-function-calls exist), and create a duplicate of the *.java file, which will subsequently processed by javadoc.
So for every piece of example code, there will be likely two, and possibly more of these string-function-calls. There may be more, because I may want to show different slices of the same example, in different contexts--perhaps the whole example in the overall class JavaDoc block, and snippets from it in the relevant functions in that class.
I have already written the process that parses the source code (the source code containing the JavaDoc blocks, which is separate from the one that reads the example-code), and re-outputs its source-code blindly with insert example-code here and insert example-code-output here markers.
I'm now at the point where I have this string-function-call in an InsertExampleCode object, in a string-field. Now I need to do as described at the top of this question. Figure out which function they want to invoke, and do so.
Change the # to a dot (.), write a class definition around it so that you have a valid Java source file, include tools.jar in your classpath and invoke com.sun.tools.javac.Main.
Create your own instance of a ClassLoader to load the compiled class, and run it (make it implement a useful interface, such as java.util.concurrent.Callable so that you can get the result of the invocation easily)
That should do the trick.
The class I created for this, called com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature, is a significant piece of Codelet, used to translate the "customizer" portion of each taglet, which is a function that customizes the taglet's output.
(Installation instructions. The only jars that must be in your classpath are codelet and xbnjava.)
Example string signatures, in taglets:
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%eliminateCommentBlocksAndPackageDecl()}
The customizer portion is everything following the percent sign (%). This customizer contains only the function name and empty parameters. This implies that the function must exist in one of a few, strictly-specified, set of classes.
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%lineRange(1, false, "Adder adder", 2, false, "println(adder.getSum())", "^ ")}
This specifies parameters as well, which are, by design, "simple"--either non-null strings, or a primitive type.
{#.codelet.and.out com.github.aliteralmind.codelet.examples.adder.AdderDemo%com.github.aliteralmind.codelet.examples.LineRangeWithLinksCompact#adderDemo_lineSnippetWithLinks()}
Specifies the explicit package and class in which the function exists.
Because of the nature of these taglets and how the string-signatures are implemented, I decided to stick with direct string parsing instead of dynamic compilation.
Two example uses of SimpleMethodSignature:
In this first example, the full signature (the package, class, and function name, including all its parameters) are specified in the string.
import com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature;
import com.github.xbn.lang.reflect.InvokeMethodWithRtx;
import java.lang.reflect.Method;
public class SimpleMethodSigNoDefaults {
public static final void main(String[] ignored) {
String strSig = "com.github.aliteralmind.codelet.examples.simplesig." +
"SimpleMethodSigNoDefaults#getStringForBoolInt(false, 3)";
SimpleMethodSignature simpleSig = null;
try {
simpleSig = SimpleMethodSignature.newFromStringAndDefaults(
String.class, strSig, null, null,
null); //debug (on=System.out, off=null)
} catch(ClassNotFoundException cnfx) {
throw new RuntimeException(cnfx);
}
Method m = null;
try {
m = simpleSig.getMethod();
} catch(NoSuchMethodException nsmx) {
throw new RuntimeException(nsmx);
}
m.setAccessible(true);
Object returnValue = new InvokeMethodWithRtx(m).sstatic().
parameters(simpleSig.getParamValueObjectList().toArray()).invokeGetReturnValue();
System.out.println(returnValue);
}
public static final String getStringForBoolInt(Boolean b, Integer i) {
return "b=" + b + ", i=" + i;
}
}
Output:
b=false, i=3
This second example demonstrates a string signature in which the (package and) class name are not specified. The potential classes, one in which the function must exist, are provided directly.
import com.github.aliteralmind.codelet.simplesig.SimpleMethodSignature;
import com.github.xbn.lang.reflect.InvokeMethodWithRtx;
import java.lang.reflect.Method;
public class SimpleMethodSigWithClassDefaults {
public static final void main(String[] ignored) {
String strSig = "getStringForBoolInt(false, 3)";
SimpleMethodSignature simpleSig = null;
try {
simpleSig = SimpleMethodSignature.newFromStringAndDefaults(
String.class, strSig, null,
new Class[]{Object.class, SimpleMethodSigWithClassDefaults.class, SimpleMethodSignature.class},
null); //debug (on=System.out, off=null)
} catch(ClassNotFoundException cnfx) {
throw new RuntimeException(cnfx);
}
Method m = null;
try {
m = simpleSig.getMethod();
} catch(NoSuchMethodException nsmx) {
throw new RuntimeException(nsmx);
}
m.setAccessible(true);
Object returnValue = new InvokeMethodWithRtx(m).sstatic().
parameters(simpleSig.getParamValueObjectList().toArray()).invokeGetReturnValue();
System.out.println(returnValue);
}
public static final String getStringForBoolInt(Boolean b, Integer i) {
return "b=" + b + ", i=" + i;
}
}
Output:
b=false, i=3

How to test method that only print out message

I have method that print winner in the class Game:
public void getWinner(String winner){
System.out.println("WINNER IS " + winner);
}
How can I test this method so far I have:
Game gm = new Game(); // it is declared in #before
#test
public void test(){
ByteArrayOutputStream outContent = new ByteArrayOutputSystea();
System.setOut(new PrintStream(outContent));
gm.getWinner(Bob);
assertEquals("WINNER IS Bob",outContent.toString());
}
I have an error message that say
org.unit.ComparisonFailuter expected:<WINNER IS Bob[]> but was: <WINNER IS Bob[
]>
Well could you please give me a tip on how to test getWinner method
omg don't do it! you don't have to test the println method. guys from sun and oracle have already done that - you can be sure it works. all you have to test is that you pass the right string to to that method. so refactor your code and create a function that return the desired string and test only that method by simple string comparison
From the documentation:
public void println(String x)
Prints a String and then terminate the line. This method behaves as though it invokes print(String) and then println().
So when you print the line in the method, there's a line separator after it which is defined as so:
The line separator string is defined by the system property line.separator, and is not necessarily a single newline character ('\n').
So you can either add a hardcoded line separator to your expected output, or you could use the following code to get the separator for the current system and append that.:
System.getProperty("line.separator");
A mockist approach:
#Test
public void testGetWinner()
{
// setup: sut
Game game = new Game();
PrintStream mockPrintStream = EasyMock.createMock(PrintStream.class);
System.setOut(mockPrintStream);
// setup: data
String theWinnerIs = "Bob";
// setup: expectations
System.out.println("WINNER IS " + theWinnerIs);
// exercise
EasyMock.replay(mockPrintStream);
game.getWinner(theWinnerIs);
// verify
EasyMock.verify(mockPrintStream);
}
Pro: You don't need to care what System.out.println() does, in fact if the implementation changes your test will still pass.
I think you try to compare to strings with == when you should use .equals(). The strings are stored i a constant pool, but in this case you read a string from somewhere else, which not nescessarily goes into the constant pool.
Try
assertTrue(outContent.toString().equals("WINNER IS Bob"));
or whatever your testing library calls it.
which looks for the characters in the String instead of the memory address ("ref") of the String.

What to return when printing out Objects using the toString method and a for each loop?

I currently have a toString method, similar to the one below. Please ignore that the Objects are only temporarily named. I have done this so that there is no confusion between the types of each variable etc.:
#Override
public String toString() {
for(Object object : ArrayList) {
System.out.println("This object is a " + object.getVariableA() + " and a " + object.getVariableB() + ".");
}
return null;
}
However the toString method requires me to return a value. I would obviously just want to return the Strings that I'm printing, although if I place a return statement there, it will only print one Object and not all of the ones I am looping through. What would be the best way to print all these values and not simply return null as I don't want this printing out after all the Objects? I also want to ensure that each of these Objects are printed on separate lines like they currently are so please don't suggest solutions that include one long joined String without line breaks as this is not suitable in this situation.
Thanks in advance!
toString shouldn't output anything at all. Its job is to return an appropriate string representation of the relevant object, not to output that representation anywhere. That's outside its problem domain.
Instead, build and return a string (probably by using a StringBuilder).
E.g., something like:
#Override
public String toString() {
StringBuilder sb = new StringBuilder(some_appropriate_size);
for(Object object : ArrayList) {
sb.append("This object is a ")
.append(object.getVariableA())
.append(" and a ")
.append(object.getVariableB())
.append(".\n");
}
return sb.toString();
}
I also want to ensure that each of these Objects are printed on separate lines like they currently are so please don't suggest solutions that include one long joined String as this is not suitable in this situation.
The above puts the items from the array list on separate "lines" (via the \n). But "one long joined String" is the only appropriate thing for toString to do. If you want a different result, you must use a different method, rather than breaking the contract of toString.
You could create a String and add what you want each iteration:
#Override
public String toString() {
String result = "";
for(Object object : ArrayList) {
result += "This object is a " + object.getVariableA() + " and a " + object.getVariableB() + ".\n");
}
return result;
}
Don't forget to add the "\n" new-line character, so you print each "partial result" in one different line.
You state in your question that:
I also want to ensure that each of these Objects are printed on separate lines like they currently are so please don't suggest solutions that include one long joined String as this is not suitable in this situation.
Then you probably shouldn't be using toString(); that's not what's it's for. It is for returning a single string that is some representation of the object. It should never be outputting anything.
Add a getter to your class that returns the List of objects, output them as you would like. If you really wanted to make the class self-printing, add a print(OutputStream os) method that takes the supplied OutputStream (or maybe a PrintStream instead) and will do so.

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

Categories