A few days ago I've stumbled upon a blog post "Modern Best Practices for
Testing in Java" (
https://phauer.com/2019/modern-best-practices-testing-java/) and learned a
lot of new things from it.

In a lot of cases, how testing is done in Druid goes against these best
practices:
 - "Given, When, Then" structure: in Druid, rarely followed or clarified
via blank lines.
 - Use the Prefixes “actual*” and “expected*: not in Druid, usually it's
something1 / something2 and what is expected and what is actual is unclear
(and sometimes confused in assert() args)
 - Heavily Use Helper Functions: used sometimes in Druid, but by far not
enough,  in my perception
 - Don’t Extend Existing Tests To “Just Test One More Tiny Thing: not
everywhere, but in a lot of places this practice is abused in Druid tests
 - Insert Test Data Right In The Test Method:  test data is often inserted
in @Before in Druid
 - Favor Composition Over Inheritance: there are some hierarchies in Druid
 - Don’t Write Too Much Logic: Druid tests feel like *all*
boilerplate/noise/scaffolding logic
 - Don’t Use In-Memory Databases For Tests: Druid does toy database Derby
for tests :)
 - Use AssertJ: Druid doesn't use AssertJ (I've just added AssertJ to
dependencies in https://github.com/apache/incubator-druid/pull/8564 in one
module)
 - Use JUnit5: ¯\_(ツ)_/¯
 - Mock Remote Service: in Druid, we don't use OkHttp's MockWebServer
 - Use Awaitility for Asserting Asynchronous Code: in Druid, we use ad-hoc
while loops with sleep(10), sleep(100), etc.
 - Separate Asynchronous Execution and Actual Logic: I think, this is one
of the most important points. In Druid, in almost all cases, execution
management (thread pools), dependency injection (and dependencies in
general), and the "core" logic are alloyed in a single class. This makes
testing the core logic very painful with a lot of mocking and setup
ceremony, breaking frequently. Following the Humble Object pattern (
http://xunitpatterns.com/Humble%20Object.html) would make writing *and
supporting* much less laborious.

These things cannot be fixed in one day because test code has a lot of
inertia, but I believe to improve developer productivity all active Druid
developers should start using most of these practices in all new tests
being written, demand other contributors align their tests with these
practices in code reviews, and take some time to think about strategic ways
to refactor a bulk of existing tests with little effort
(semi-automatically) that can at the same time improve their quality even
by a little.

In my experience, wrestling with tests is a huge (often the biggest) part
of changing existing code in Druid, so we pay a huge productivity tax by
not paying enough attention to the quality of the test codebase.

Reply via email to