I discovered FEST asserts just yesterday and they seem to be very
interesting! I implemented something similar for internal purposes but
it is not nearly as good as fest.
However, there is something important that inhibits extending
assertions. Assertions class and Assert classes are final and it:
- makes it hard to introduce assertions for custom types
- makes it hard to introduce new assertion methods for already handled types
The way I imagine working with FEST assertions in my projects is that
I have my own Assertions class. If someone needs a custom assertion he
can just create overloaded assertThat() method in Assertion class. If
someone needs a custom assertion method for common type (e.g.
collection) he can create a new assertThat() method that hide the one
from the parent class. New assertThat() would return
MyOwnCollectionAssert with extra features.
This the example from the FEST wiki:
ServerSocketAssertion socket = new ServerSocketAssertion(server.getSocket());
assertThat(socket).isConnectedTo(2000);
//I'd prefer:
assertThat(socket).isConnectedTo(2000);
You might also consider providing humanized aliases for satisfies(),
e.g. is(), has(), etc.
assertThat("hello").as("Greeting").satisfies(isUppercase());
//I'd prefer:
assertThat("hello").as("Greeting").is(uppercase());
Hope this helps!
Szczepan Faber
http://mockito.org
szczepiq wrote:
>> Assert classes are final because of the lack of "self-types" in Java. For
>> example, if we MyStringAssert subclasses StringAssert, we need to override
>> all the methods in StringAssert to return MyStringAssert instead of
>> StringAssert. I found that it is very easy to forget to do so, and it may be
>> a source of confusion. I thought that by using composition instead of
>> inheritance, we could remember to return the correct type from the assertion
>> methods.
>>
>
> Hmmmm, I see point now. I would still recommend to un-finalize the
> assert classes... Internally, you can probably figure out some way of
> verifying if you didn't forget to override (like extensive functional
> test suite, PMD rule or jUnit test that reflects the methods, etc.).
> Externally, I'd like to have an easy way to extend assertions on
> already handled types.
As far as I understand, the two alternatives are:
1. use final assert classes and composition/delegation. "Finalness"
makes sure that we do not forget to define appropriate return types for
the derived assert classes
2. use non-final assert classes and take additional measures to make
sure we did not forget to override the base methods with more specific
return types
Comparing them, a FEST user defining an extension to an assert class has
to (correct me if I'm wrong):
1. define new check methods calling a delegate (assert<Condition>;
return this;)
2. define overridden methods calling the supertype's method and cast the
result to <SelfType>.
If you forget to override the super methods in 2), you cannot use the
new check methods after the first call to a check method of the
superclass, since it returns a more generic assertion class. If you do
override the method, the effort required to do this is nearly the same
as for option 1.
E. g. option 2 for XmlAssert extends StringAssert:
XmlAssert.assertThat(someXml).isNotEmpty().isSimilarTo(someOtherXml)
This will fail, since isNotEmpty will return a StringAssert instance
which does not know isSimilarTo().
In my eyes, this is a potential source of confusion to the casual FEST
user. Besides, the difference between defining an overridden method
calling super+casting on the one hand and defining a new method
delegating to the base class seems neglectable to me.
One disadvantage: you commonly have to define *new* methods in the
custom/extension assert classes before you can delegate. These methods
should have the same signature as in the base class. It's probably quite
error-prone to do this manually. Most modern IDEs have support for
implementing methods of java interfaces semi-automatically. If we start
to define the API of the base classes as interfaces, it should become
more easy to both implement the delegating methods and keep the
interface of the extension aligned with the one of the base classes.
Given we want to make sure that the API is consistent (i. e. always
returns <SelfType>):
- finalizing the base classes *guarantees* this
- alternative 1 does not significantly reduce implementation effort
I'd opt to keep finalized assert classes. Since extension of FEST seems
to be a source of questions and extension requests, we might do good in
agreeing on and documenting some "best practices" on how this should be
done.
>> "warning" from Eclipse (I think this warning is just a setting in the
>> compiler, and not a default)
>>
>
> I wouldn't worry about the warning as it's only when someone calls the
> static method on the instance. Why would anyone do it?
>
Here, we're also using our own custom assertions without extending class
Assertions at all. We just put a static factory method "assertThat" into
each new assert class and do a static import of this method in each test
we want to use the class in. This works quite smoothly. To us, the
Assertions class is basically a central factory provided by the
FEST-Assertions library which we do not fiddle about at all.
I invite you to discuss whether a single static factory for all your
assert classes in your project is the best approach. Remember that this
class would have to reference all your classes you'd like to write
assertions for, since assertThat takes the actual value as an argument.
This factory class becomes a dependency magnet. It will be hard to
manage these dependencies. Try to imagine your assertion factory
referencing classes from all over your domain model, your technical
infrastructure, ...
The solution using a single static factory method in the assert class
seems more appealing to me.
>
>> Add a "is" and "isNot" methods that do the same as "satisfies" and
>> "doesNotSatisfy"
>>
>
> Cool. I also recommended 'has' alias because sometimes it's hard to
> express the assertion with 'is', e.g. page.hasHeadline() vs
> page.isPageWithHeadline(). Again, it's up to you. I'm not interested
> too much in custom conditions as I think custom assertions are way
> more powerful.
>
I like FEST assertions for its simple API. I think it is a significant
plus to attract new users. We should take care not to complicate the api
unnecessarily. When I was new to the FEST API, I even considered it
strange to have both as() and describedAs(), which do exactly the same
thing. I later understood this is a technical necessity to allow usage
of FEST in groovy. I'd love to see FEST keep its API as simple as possible.
Think about http://c2.com/xp/OnceAndOnlyOnce.html
I'd agree that satisfies/doesNotSatisfy sounds like math, not like a
check of domain conditions. Nevertheless, I feel that this basic concept
of a condition/predicate is absolutely valid to use when defining tests.
We also kind of agree that "assertThat" is fine for all tests, however
your business analyst would probably express this a bit different (e. g.
"makeSureThat", "itMustAlwaysBeTheCase"). If someone asked me, I'd opt
not to include each and every variant which a natural language might
have developed for the same meaning over time, but to convey this
meaning using exactly one, carefully chosen word.
Kind regards,
Ansgar
After thinking about it for a while, I must admit: Szczepan, you are right.
For a library which should be useful in a large number of projects, with a large number of different requirements, giving the user choice is the preferrable way to go.
Kind regards
Ansgar