Intruduce BiMatcher hierarchy
See original GitHub issueA good feature would be introducing BiMatcher
hierarchy (analog of org.hamcrest.Matcher
, but consuming 2 args). Alternatively standard Comparator
can be used for this purpose. Also BiPredicate
can be used (this will tie JUnit to Java-8, but will add out-of-the-box AND/OR/NOT logic).
A couple of notes for all further code:
BiMatcher
,BiPredicate
andComparator
names will be used interchangeably.- All the code is not final and just a general idea.
Main methods to add in Assert
class:
assertThat(T expected, T actual, Comparator<T> biMatcher);
assertThat(String message, T expected, T actual, Comparator<T> biMatcher);
(maybe not necessary, see below).- Multiple
assertArray()
methods (see below).
The motivation: sometimes you need to do a lot of similar checks against batch of objects. Existing functionality allows it, but with a lot of additional code (loops and conditions). With Comparator
it can be done with less code and with higher flexibility.
Real example (from which I actually started to think about this feature): need to test all objects in the array are the same or not the same (depending on a boolean flag).
With current JUnit abilities it will look like: shorter:
for (int i = 0; i < elements.length - 1; i++) {
if (checkSame) { // Same
assertSame("elements must be the same.", elements[i], elements[i + 1]);
} else { // Not same
assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
}
}
better performance (but who cares about performance in tests):
if (checkSame) { // Same
for (int i = 0; i < elements.length - 1; i++) {
assertSame("elements must be the same.", elements[i], elements[i + 1]);
}
else { // Not same
for (int i = 0; i < elements.length - 1; i++) {
assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
}
}
Also we can implement own Comparator
and use assertTrue()/assertFalse()
, but the code for creating comparators will also be big (unless we use lambdas).
With new approach code will look much shorter and cleaner as for me, like this:
Comparator<T> comparator = checkSame ? BiMatchers.same() : BiMatchers.notSame();
String message = checkSame ? "elements must be the same." : "elements must be not the same.";
for (int i = 0; i < elements.length - 1; i++) {
assertThat(message, elements[i], elements[i + 1], comparator);
}
To make it even shorter, we can extend Comparator
to MessagedComparator
with optional message
property, so JUnit will take it from there somehow. Like this:
MessagedComparator<T> comparator = checkSame ? BiMatchers.same("elements must be the same.") : BiMatchers.notSame("elements must be not the same.");
for (int i = 0; i < elements.length - 1; i++) {
assertThat(message, elements[i], elements[i + 1], comparator);
}
Consequences are very far-reaching:
- The
Assert
class will be very flexible in general, because you can use any standard or own comparator without much code. - As a result of previous, lambdas can be used more easily to inject any conditions.
- Additional logic cascading (AND, OR, NOT) can be added out-of-the-box (something similar to
org.hamcrest.CoreMatchers
). Currently for any new mini-feature a new method required, e.g.assertNotSame()
next toassertSame()
etc. Or again we needassertTrue()/assertFalse()
methods. - The whole
Assert
class can be refactored to unified form. Just one example:
public static void assertSame(String message, Object expected, Object actual) {
assertThat(message, expected, actual, BiMatchers.same())
}
public static void assertThat(String message, T expected, T actual, Comparator<T> biMatcher) {
...
}
or as already mentioned:
public static void assertSame(String message, Object expected, Object actual) {
assertThat(expected, actual, BiMatchers.same(message))
}
public static void assertThat(T expected, T actual, MessagedComparator<T> biMatcher) {
...
}
assertArray()
methods can be added (thus old methods refactored). E.g.:
public static void assertArrayEquals(boolean[] expecteds, boolean[] actuals) {
assertArray(expecteds, actuals, BiMatchers.equals());
}
public static void assertArray(boolean[] expecteds, boolean[] actuals, Comparator<Boolean>) {
...
}
So for example you can check all elements in 2 arrays are same as easy as:
Assert.assertArray(array1, array2, BiMatchers.same());
Also array-length or other checks can be controlled separately. Pseudocode:
Assert.assertArray(array1, array2, and(sameLength(), same()));
Issue Analytics
- State:
- Created 6 years ago
- Comments:9 (7 by maintainers)
Top GitHub Comments
@gitIvanB JUnit’s philosophy is to provide a good, extensible framework for writing tests for Java in Java. Being an extensible framework, we often see other open source projects extend the framework and/or provide rich testing libraries that can be used with JUnit.
For assertions, there are several projects that provide a richer set of assertions, sometimes in a way that is extensible. For example, there is Google Truth (https://github.com/google/truth), FEST (https://github.com/alexruiz/fest-assert-2.x), AssertJ (http://joel-costigliola.github.io/assertj/) and Hamcrest (http://hamcrest.org/JavaHamcrest/).
Because there are so many amazing assertion libraries out there I have been reluctant to accept new feature requests for
org.junit.Assert
. I wouldn’t consider it frozen (though I would considerjunit.framework.Assert
frozen). In fact, 4.13 will introduceassertThrows
. But since we will never have as rich a set of assertions as those projects we feel comfortable providing the basics.Put another way, heathy communities have formed around the use and maintenance of those projects, so I think we should embrace and support them.
I can’t speak for JUnit5, but I imagine the original developers felt they couldn’t release the next generation of JUnit without a good foundation of assertion methods (or perhaps they felt like the best way to ensure that the new framework was functional and polished was to test JUnit5 using JUnit5). New programming practices caused them to make different decisions on what a basic assertion API would look like than Kent, Erich and David did, so you will see some differences. But you will likely never see in JUnit as rich a set of assertions as with Hamcrest or an extensible DSL like Truth.
I leave it to the JUnit5 team to decide what types of changes they will consider. If you feel strongly that the methods you are proposing are important as a foundation of writing good tests feel free to create an issue there.
@gitIvanB Thanks for opening the issue. Like @kcooney, I also think this would best be addressed by an assertion library, e.g. AssertJ.