-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
OLD Assertion API
- Allows developers to code more explicitly. Somewhat self-documenting.
- Better reporting
- Improves readability and structure
- Projects like Hamcrest, Fest, AssertJ, and Truth already do this job quite well.
- Designing a new API for assertions is much harder than many people think.
- The core JUnit code doesn't need to be made aware of the specific assertion library that the developer uses, so assertion libraries can be released as a separate project.
- Methods are easy to understand and abundant
- Promotes safe coding practices when possible (e.g. floating point comparison)
- Uses a powerful and extensible destructuring and reporting DSL (Hamcrest)
Java 8 Friday: Most Internal DSLs are Outdated (also referenced on the Materials page)
Pros:
- Minimal code
- Fewer bugs
- No maintenance
Cons:
- Failed tests are hard to diagnose and debug. (No helpful error messages. Stack traces involving lambdas are most often not helpful.)
- Intentions are sometimes unclear (what's this
Stream
ofMatcher
s black magic?)
The cons only apply when one uses only predicates, but instead of predicates we could use function of type T ->
Option[Description]
for asserting onT
. schauder
we could provide bridges to Hamcrest and other popular assertion libraries as optional dependencies. After seeing AssertJ, I'm not very happy with JUnit pushing Hamcrest schauder
Need to find a way to bridge between Hamcrest Matcher
s and FunctionalInteface
s. This includes Function
s as well, because all matchers that take an argument can effectively be seen as applying a transform on the input.
Pros:
- User keeps existing investment in Hamcrest while benefiting from lambdas
Cons:
- Implementation/syntax could be confusing
Matcher
has two jobs: assert and describe, so has to find a way for a Predicate
to do both.
Java 8 allows almost any expression to be annotated, so any lambda expression can come with a simple description in an annotation.
According to stackoverflow, you can get the name of a serialized function reference (works in Oracle java, but not in Eclipse), so function references can be self-describing.
E.g. one can write assertThat(x, after(X::getSomething, is(y))
and the error message can be something like "Expecting ${x.toString()} after X::getSomething is ${y} but is ${z}".
- @saff: I spent many weeks trying to get something magical like this to work in a way that I could recommend, and I don't think it's doable. (a) As mentioned above, serialization of lambdas appears to be implementation-dependent. (b) it breaks down when the user wants to talk about operations beyond just a single method call, something that happens a lot of the time in Java 8 code.
Traditionally, expanding the vocabulary of a fluent system (e.g. AssertJ) requires implementing special classes and using loads of generic tricks. This is not simple nor lambda friendly. I propose we create a new one with minimal built-in asserts but be super extensible through the following methods:
- Ad-hoc definition: supply a
Predicate
/Function
with some descriptions:assertThat(person).whose(p => p.getAge(), "age").is(i => i > 30, "greater than 30)
- Poor man's traits: extention through combining interfaces:
interface Person {
String getName();
int getAge();
}
interface Assertions<T> {
T actual();
default void is(T expected) {
// ...
}
// Ad-hoc, other defaults...
}
interface IntegerAssertion extends Assertions<Integer> {
default void isGreaterThan(int max) {
// ...
}
}
interface PersonAssertion extends Assertions<Person> {
default IntegerAssertion whoseAge() {
return actual()::getAge;
}
}
// Say the build-in library is:
interface Asserts {
default PersonAssertion assertThat(Person p) {
return () -> p;
}
}
// One can trivially extend:
interface MyAsserts extends Asserts {
interface BetterPersonAssertion extends PersonAssertion {
default Assertions<String> whoseName() {
return actual()::getName;
}
}
@Override
default BetterPersonAssertion assertThat(Person p) {
return () -> p;
}
}
// Usage:
class PersonTest implements MyAsserts {
@Test
public void checkSoemthing() throws Exception {
Person p = null;
assertThat(p).whoseAge().isGreaterThan(30);
}
}
Pros:
- Clear and simple
- Library-neutral: Can easily bridge any existing assertion library (Hamcrest, AssertJ, etc.)
- Augmentable/Upgradeable: As everything is glued together using interfaces only, one can overlay an entirely different set of code (e.g. to improve error reporting, to bridge another assertion library) on top without affecting user code.
Cons:
- Yet another library.
- High levels of overlap with AssertJ/Google Truth
Most test frameworks (including non-Java ones) include assertion facilities and searches for "JUnit assert" [eclipse](https://www.google.co.uk/trends/explore#q=junit assert%2C assertj%2Bfest assertion%2Bgoogle truth assertion%2BCheck4J%2C hamcrest) other popular libraries combined, so it's safe to say the users expect one to be there. However, as part of a test framework, we should probable aim for a simple and extensible one. The current Assert
class achieves the latter with Hamcrest, but it is not lambda friendly. So I think we should provide another way of extension. - @billc.cn
The question isn't whether JUnit should have an assertion API (it has one already). The question is whether we should build a new assertion API and ship it as part of JUnit Lambda. Again, the core JUnit code would not depend on such an API, so it is a good candidate for functionality that can be created and grow outside of JUnit-core. If you are really passionate about making such an API, how about creating a new project under github/junit-team for this and experiment there? If people love it, we can add it to JUnit-Lambda. - @kcooney
I would prefer more effort to be spent on the "API for Running and Reporting Tests" than on a new assertion API/library. With AssertJ, Hamcrest and Truth there are three assertion libraries under active development. At least AssertJ (never used Truth and have not used Hamcrest for years) is far ahead of anything JUnit could offer. - @PascalSchumacher