Provide ability to create custom assumption types in the public API
Feature summary
I am currently writing a library that enables the writing of integration tests that run Java annotation processors and invokes the compiler. Part of this is allowing the user to perform assertions on the compilation result. This is being implemented currently by extending the AbstractAssert class that AssertJ provides.
For example:
var sources = RamPath
.createPath("sources")
.createFile(
"org/example/HelloWorld.java",
"""
package org.example;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
"""
);
var result = Compilers
.javac()
.addSourcePath(sources)
.release(11)
.compile();
assertThatCompilation(result)
.isSuccessfulWithoutWarnings()
.classOutput()
.file("org/example/HelloWorld.class")
.exists()
.isNotEmptyFile();
...as part of this, I'd like to be able to provide the ability to assume criteria as well as assert criteria. I noticed that the Assumptions class provides internal detail to create the proxies needed to wrap an AbstractAssert, but unfortunately this API does not appear to be public.
Is there a way to do this for custom assertions, or is it possible to add this into consideration for a future release?
Example
class MyAssertion extends AbstractAssertion<MyAssertion, String> {
...
MyAssertion isCool() {
if (!"cool".equals(actual)) {
failWithMessage("That isn't cool!");
}
return myself;
}
}
public static MyAssertion assertThat(String string) {
return new MyAssertion(string);
}
public static MyAssertion assumeThat(String string) {
return Assumptions.fromAssertion(new MyAssertion(string));
}
such that Assumptions.fromAssertion can just return a Proxy of some description like the following:
// Not too familiar with ByteBuddy internals, so this is just a pseudocode example.
public class AssumptionProxy implements Proxy {
private final AbstractAssert assertion;
...
@Override
public Object intercept(Method method, Object[] args) throws Throwable {
try {
return method.invoke(assertion, args);
} catch (InvocationTargetException ex) {
if (ex.getCause() instanceof AssertionError) {
// This would cater for junit4, testng, etc like the current internal stuff in
// assertj would.
throw new TestAbortedException(ex.getCause());
} else {
throw ex;
}
}
}
}
That's a fair request, we need to look into details how to do that and figure out the best option here.
From https://github.com/assertj/assertj/issues/2713#issuecomment-1199482625, we should consider the following points:
- Custom assertions do not require a
public1-arg constructor to work as hard assertions. - AssertJ has no concrete documentation or guidelines on how third-party libraries should write custom assertions to be compatible with soft assertions.
- Connecting custom assertion classes with AssertJ core components to get soft assertions support is currently a burden on users and there is no guarantee that it will work (as with XMLUnit).