javafluent-interfacefest

Extending fluent interface


I'm starting using Fluent Assertions and I like it a lot, but wonder if it's possible to extend the existing tests in a general way like this:

These are just examples what I could need soon. I can do some workaround for each of them, but then I lose the readability and the easy of use of the fluent interface.

My last example is meant to throw iff both x.isIn(someSet) and x.isNull() do.


Solution

  • Here is a post by the author about opening up his API for extending assertions on already handled types. Lesson #1 in particular discusses the change to un-finalize classes. The post also gives an example of sub-classing StringAssert as MyStringAssert.

    However, it looks like you cannot extend classes such as StringAssert in a way that maintains the "fluency" of the API. The StringAssert class isn't final, but still it doesn't allow you to parameterize its type (i.e. the "this" type that's returned by methods in StringAssert itself) in subclasses. For example, let's say you add a method checkFoo in MyStringAssert. As you discovered, the following is invalid because the original StringAssert methods return StringAssert:

    new MyStringAssert("abcd").contains("a").checkFoo(); // compile-time error!
    

    You only can call your subclass's methods first, which is valid but kind of lame:

    new MyStringAssert("abcd").checkFoo().contains("a"); // compiles
    

    You might consider contacting the author, or even submitting a patch to his git project. A possible solution would be to add the parameterized type back into StringAssert, and also provide the StringAssert concrete type via an anonymous subclass within Assertions.assertThat(String), which is the recommended entry point anyway. Then, everybody else can subclass StringAssert as you described. I haven't tested this suggestion either, but it seems to make sense...