1

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:

  • add method hasSizeAtLeast(int limit) in GroupAssert
  • add method startsWithIgnoringCase(String prefix) in StringAssert
  • use alternatives like x.either().isIn(someSet).or().isNull()

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.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • I would understand **either ... or ...** as a XOR connection, i.e. it should throw off iff exactly one of them throws. (But I'm not a native english speaker.) – Paŭlo Ebermann Jul 17 '11 at 11:02
  • Neither am I, but I think you're not exactly right. AFAIK, "either-or" may be also used when you want to stress the existence of the choice. Also `hamcrest` uses it this way. – maaartinus Jul 17 '11 at 21:13
  • Link to fluent assertions is 404. – james.garriss Jul 28 '15 at 18:19

1 Answers1

2

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...

jtoberon
  • 8,706
  • 1
  • 35
  • 48
  • Thank you. The link actually shows, that extending `StringAssert` does NOT really work, since you can't return `MyStringAssert`. So you can't get `new MyStringAssert("abcd").contains("a").hasSizeAtLeast(3);` working using inheritance. – maaartinus Jul 17 '11 at 21:34
  • Hmm, you're right. Apologies for the confusion, I should have tried this first! I will my response to remove the misinformation and provide a few suggestions. – jtoberon Jul 22 '11 at 14:22
  • No problem, you've helped me a lot as I was being completely clueless about where to start. I'm quite sure your suggestion makes sense. It's a pity, Java provides only such a terrible workaround for [self-types](http://www.jroller.com/scolebourne/entry/java_7_self_types) forcing everybody to either pollute the code with otherwise useless generic parameter or to lose flexibility. – maaartinus Jul 22 '11 at 19:26