2

I am using a JUnit Rule to immediately re-run any failed tests. My extra requirement is, if the re-run also fails, determine whether they failed for the same reason.

To do this I've adapted the code from this answer to keep a record of the failures and compare them. However, the comparison (.equals) always evaluates to false despite them failing for the same reason. What is the best way to go about this?

  private Statement statement(final Statement base, final Description description) {
    return new Statement() {
      @Override
      public void evaluate() throws Throwable {

        for (int i = 0; i < retryCount; i++) {
          try {
            base.evaluate();
            return;
          } catch (Throwable t) {
            System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed");

            // Compare this error with the one before it.
            if (errors.size() > 0) {
              if (t.equals(errors.get(errors.size() - 1))) {
                System.out.println("The error is the same as the previous one!");
              } else {
                System.out.println("The error is different from the previous one.");
              }
            }

            errors.add(t);
          }
        }
        System.err.println(description.getDisplayName() + ": giving up after " + retryCount
            + " failures");

        // Throw most recent error.
        throw errors.get(errors.size() - 1);
      }
    };
  }
Community
  • 1
  • 1
bobble14988
  • 1,749
  • 5
  • 26
  • 38
  • 5
    What is the basis that *you* want to compare them on? What should "the same reason" be? (Same stack traces? Same `throw` statement? Identical method arguments which caused the failure?) The correct way could be many things. – Radiodef May 15 '15 at 13:39
  • 2
    If your tests can fail for different reasons without any code change, they are not very reliable – nicopico May 15 '15 at 13:41

4 Answers4

4

Use the instance of if want to know only if the is a type of any class, like:

if( t instanceof Throwable){
   //... 
}
Siloé Bezerra Bispo
  • 2,056
  • 1
  • 17
  • 31
3

Equality for a Throwable is defined as "the same Throwable". (It compares references.)


"Same reason" is something you need to think about and define for your application.

One way we could define it is "the same type and loosely the same throw statement":

static boolean sameTypeAndLine(Throwable t1, Throwable t2) {
    if (t1.getClass() == t2.getClass()) {
        StackTraceElement[] trace1 = t1.getStackTrace();
        StackTraceElement[] trace2 = t2.getStackTrace();
        return trace1[0].equals(trace2[0]);
    } else {
        return false;
    }
}

But that still has ambiguity:

  • if (bad1 || bad2) {
        // same throw site, different conditions
        throw new Exception(...);
    }
    
  • // throws NullPointerExeption
    // (was it foo or the result of bar() that was null?)
    Object baz = foo.bar().baz();
    

So, the best thing you can do is clearly define reasons for your exceptions:

class MyException {
    final Reason reason;

    MyException(Reason reason) {
        this.reason = reason;
    }

    // or a class, or whatever you need
    enum Reason {A, B, C}
}

And check against those. Then additionally, use a coding style which prevents ambiguities.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
2

Equals is not properly implemented for exceptions. You will have to compare the message and/or the stack trace yourself.

Necreaux
  • 9,451
  • 7
  • 26
  • 43
1

You can only compare:

by name

t.getClass().getName()

or by instanceof

t instanceof XXXXXXX
MrSimpleMind
  • 7,890
  • 3
  • 40
  • 45