Re: Re: JUnit 4.12-beta-1 is released

"[email protected] [junit]" <[email protected]> 09 Aug 2014 13:05:45 -0700
Newsgroups gmane.comp.java.junit.user
Message-ID <[email protected]>
Hi Kevin

I am sorry to say this but from your response it seems like my reasoning did not make any sense at all to you. - and consequently you did not understand any of my concerns. After some more thinking I will now try again to make my point by using more detail and some example scenarios - but the best would probably be if we could spend some time together so that I can carefully demonstrate what kind of problems that e.g. 3rd-party runner developers have to deal with. - Please let me know if you are about to spend time in Stockholm any time soon ...

> This was a hard choice. JUnit4ClassRunner was starting to become a maintenance burden. When
> evaluating our options, we realized that JUnit4ClassRunner has been deprecated for six years,
> and debated whether we should delete it.

If JUnit4ClassRunner has become "a maintenance burden" then I suggest you stop maintaining it - I have absolutely no problem with that. - But I have no idea how this maintenance burden could possibly give you reason to DELETE IT? To preserve functionality for a deprecated class could be difficult - but to preserve compile-compatibility, which I was clearly asking for, is very simple!

We are all familiar with another project that, just like JUnit, has a number of 3rd-parties that depend on it. Its latest API-documentation can be found here:
http://docs.oracle.com/javase/8/docs/api
It has been under active development for about 20 years, i.e. slightly longer than JUnit, and has naturally been forced to deal with deprecation scenarios a number of times. Over the years it has happened that behavior-compatibility has been broken (i.e. modified functionality). I also think there are a few cases when method- or constructor-overloading has broken the compile-compatibility but those problems were easily resolved with explicit argument casting. But AFAIK there are no examples on broken compile-compatibility because of deprecation and discontinued maintenance!

I suggest that you now carefully consider the wise deprecation strategy of this other project and also this Jedi saying:
"The choices of one shape the futures of all."
This saying is especially relevant if "one" is the JUnit project, on which so many other projects are dependent, so let's imagine a project that has been without activity for a few years and suddenly gets a new budget and development is restarted. Someone upgrades to JUnit-4.12 and consequently an unlucky someone else discovers that an important test cannot run. The unlucky (junior) developer can encounter the problem in different ways:

 - With some luck the developer encounters a compilation error:
"cannot access JUnit4ClassRunner"

 - With less luck the developer encounters a test initialization-error with a strange message:
"Type [unknown] not present"

I assume the JUnit team considered these circumstances during the "evaluating our options" phase and concluded it to be OK - but I am of the opinion that the above scenarios can "shape the futures of all" in sad directions:
1) Unlucky developer asks the team "Did anyone change something?"
2) Team answers "no"
3) Unlucky developer asks again
...
10) A team member says "I have upgraded JUnit - but that should not affect anything"
11) Unlucky developer responds "I guess not"
12) Unlucky developer asks again
...
20) Unlucky developer tries to downgrade JUnit - and problem was found!!
21) Project runs over budget
...
999) Turns out an important goverment function was dependent on the success of the project
...
9999) Taxes are raised to cover costs of inefficient goverment functions (i.e. "the futures of all" have been shaped)

I am by no means a product lifecycle guro but I still think I can suggest a much better solution in this case. - How about simply changing the super-class of "JUnit4ClassRunner" from "Runner" to a new class that has the name "Runner_is_incompatible_with_JUnit_version_4_12_or_later" and looks similar to this:

abstract class Runner_is_incompatible_with_JUnit_version_4_12_or_later extends Runner {

  /* Constructor that throws an InitializationError! */
  protected Runner_is_incompatible_with_JUnit_version_4_12_or_later(Class<?> testClass)
  throws InitializationError {

    RunWith runWith = testClass.getAnnotation(RunWith.class);
    String runnerClassName = (runWith == null ? getClass() : runWith.value()).getName();

    throw new InitializationError(
    "Some kind of message that tries to explain for the developer that " + testClass
    + " uses runner " + runnerClassName + ", which is incompatible with JUnit-4.12, "
    + " and perhaps also give the developer a few options on how this can be dealt with etc ...");
  }
}

This solution would result in a test-failure with a much better message that IMHO helps the developer find the problem much quicker. - I also think that my suggestion would put an insignificant maintenance burden on the JUnit community, whereas simply deleting JUnit4ClassRunner is a neglective behaviour that passes on significant maintenance burdens on other projects that in turn will be less able to "shape the futures of all" in an efficient manner!

---
The above reasoning does mostly concern runners that have been developed internally, within an organisation or project, to deal with certain locally occuring circumstances.

For the general-purpose runners MockitoJUnitRunner and CallbackParamsRunner there are road-blocks that can take care of the compatibility problems before the problem is passed on to the anonymous unlucky developer - but it still is a case of passing on a JUnit "maintenance burden" to other projects.

I'd say that your analyses of these runners' dependency on JUnit4ClassRunner is flawed. You surprise me a bit by first making a correct statement saying that "JUnit4ClassRunner isn't designed to be extended" but when you investigate these 3rd-party runners you are only considering the parent-class of the API runner-classes. A hint on the design-patterns used for these runners can be found on this link:
http://en.wikipedia.org/wiki/Delegation_pattern

BTW - I am pretty sure that MockitoJUnitRunner extends Runner. - At least it does so where I have been looking:
http://docs.mockito.googlecode.com/hg/latest/org/mockito/runners/MockitoJUnitRunner.html

... where we have the latest javadoc, right? - and ...
https://github.com/mockito/mockito/blob/master/src/org/mockito/runners/MockitoJUnitRunner.java

... where we find the most recent source-code, which is well-documented and constitutes an easy-to-understand example of the delegation pattern. I cannot figure out where you would have read that MockitoJUnitRunner extends BlockJUnit4ClassRunner?

BR /Henrik Kaipe

PS
I be back with more detail and examples on the RunListener thread-safety/concurrency stuff on a separate post. - Now it is time to sleep.
DS