I am pondering over a few different ways of writing utility classes/functions. By utility I mean a part of code being reused in many places in the project. For example a set of formatting functions for the date & time handling.
I've got Java background, where there was a tendency to write
class UtilsXyz {
public static doSth(){...};
public static doSthElse(){...};
}
which I find hard to unit test because of their static nature. The other way is to inject here and there utility classes without static members.
In Dart you can use both attitudes, but I find other techniques more idiomatic:
- mixins
Widely used and recommended in many articles for utility functions. But I find their nature to be a solution to infamous diamond problem rather than utility classes. And they're not very readable. Although I can imagine more focused utility functions, which pertain only Widgets, or only Presenters, only UseCases etc. They seem to be natural then.
- extension functions
It's somehow natural to write '2023-01-29'.formatNicely()
, but I'd like to unit test some utility functions, and you cannot test extension functions, because they're static.
- global functions
Last not least, so far I find them the most natural (in terms of idiomatic Dart) way of providing utilities. I can unit test them, they're widely accessible, and doesn't look weird like mixins
. I can also import them with as
keyword to give some input for a reader where currently used function actually come from.
Does anybody have some experience with the best practices for utilities and is willing to share them? Am I missing something?
CodePudding user response:
To write utility functions in an idiomatic way for Dart, your options are either extension methods or global functions.
You can see that they have a linter rule quoting this problem:
AVOID defining a class that contains only static members.
Creating classes with the sole purpose of providing utility or otherwise static methods is discouraged. Dart allows functions to exist outside of classes for this very reason.
https://dart-lang.github.io/linter/lints/avoid_classes_with_only_static_members.html.
Extension methods.
but I'd like to unit test some utility functions, and you cannot test extension functions, because they're static.
I did not find any resource that points that the extension methods are static, neither in StackOverflow or the Dart extension documentation. Although extension can have static methods themselves. Also, there is an open issue about supporting static extension members.
So, I think extensions are testable as well.
To test extension methods you have 2 options:
- Import the extension name and use the extension syntax inside the tests.
- Write an equivalent global utility function test it instead and make the extension method call this global function (I do not recommend this because if someone changes the extension method, the test will not be able to caught).
Global functions.
To test global functions, just import and test it.
I think the global functions are pretty straightforward:
- This is the most simple, idiomatic way to write utility functions, this does not trigger any "wtf" flag when someone reads your code (like mixins), even Dart beginners.
- This also takes advantage of the Dart top-level functions feature.
That's why I prefer this approach for utility functions that are not attached to any other classes.
And, if you are writing a library/package, the annotation @visibleForTesting
may fall helpful for you (This annotation is from https://pub.dev/packages/meta).