Currently, there aren't many classes (if any) in JUnit 5 that are instantiated via _static factory methods_, as opposed to constructors. _Effective Java 2nd Edition_, Item 1 gives a number of compelling reasons for preferring static factory methods. Therefore we should consider adopting them more, skipping over those classes where there is a more compelling reason for them to have public constructors instead.
ClasspathResourceSourceClassSourceCompositeTestSourceDirectorySourceFilePositionFileSourceMethodSourcePackageSourceLoggingListener: make the existing constructor private and introduce a forBiConsumer(...) SFM.TestSources utility class (analogous to DiscoverySelectors) that will house the SFMs for TestSource implementations.from(...) SFMs in each TestSource implementation instead.slated for RC1 for team discussion
A possible regex to use to search for candidate classes (at least within IntelliJ IDEA):
public\s*(?!.*final|.*abstract)\s*class
That RegEx also works in Eclipse.
@jbduncan, do you have any particular classes in mind?
Currently, there aren't many classes (if any) in JUnit 5 that are instantiated via static factory methods, as opposed to constructors.
That's actually not true.
We have several classes with static factory methods, and in other situations the constructor is package private so that no third party could invoke them anyway.
@sbrannen
That's actually not true.
We have several classes with static factory methods, and in other situations the constructor is package private so that no third party could invoke them anyway.
Oh yes, you're right! I admit I didn't search thoroughly for offending classes when I opened the issue, so apologies.
@jbduncan, do you have any particular classes in mind?
I've identified the following list of classes which I think should be considered for the static factory method treatment.
Classes labelled with (?) are classes that I'm unsure about due to my knowledge on how JUnit 5 instantiates them being incomplete.
Although I've listed LoggingListener, which does already has some static factory methods, I notice it still has a single public constructor new LoggingListener(BiConsumer) which I think could be renamed to LoggingListener.forCustomLogger(BiConsumer) or something similar.
Furthermore, although I've also listed ToStringBuilder, I do realise that it's an internal class, so I wouldn't be too upset if it weren't given static factory method treatment for that reason. :wink:
ClassSource
CompositeTestSource
DirectorySource
DynamicContainer
ExpectedExceptionSupport (?)
ExternalResourceSupport (?)
FilePosition
FileSource
JUnitPlatform
LoggingListener
MethodSource
PackageSource
SingleTestExecutor
SummaryGeneratingListener
ToStringBuilder
VerifierSupport (?)
I also went through the (non-internal) API. Here are my lists:
Sorry @marcphilipp, I'm not entirely clear on the purpose of the lists above.
Are they identifying _existing_ classes which use either static factory methods (SFMs) or constructors, or are they identifying which classes you _think_ should have SFMs and which should have constructors?
ClassSource
CompositeTestSource
DirectorySource
FilePosition
FileSource
MethodSource
PackageSource
For these I think it might make sense to use static factory methods. @junit-team/junit-lambda Thoughts?
Already has static factory methods and a private constructor, doesn't it?
ExpectedExceptionSupport (?)
ExternalResourceSupport (?)
VerifierSupport (?)
These are instantiated by JUnit 4 and thus need a public constructor.
SummaryGeneratingListener
These do not declare a constructor and I don't foresee us adding one so they should be fine.
Has a single constructor that looks like in other libraries with similar helper classes. Thus, I'd rather keep it that way. It's already a builder, so if we ever needed to add an additional parameter we could add a builder method.
I think this one should definitely be changed because it uses both static factory methods and has a public constructor.
Are they identifying _existing_ classes which use either static factory methods (SFMs) or constructors, or are they identifying which classes you _think_ should have SFMs and which should have constructors?
Point taken. They identify _existing_ classes which use either static factory methods (SFMs) or constructors.
DynamicContainer
Already has static factory methods and a private constructor, doesn't it?
@marcphilipp Oh yep, you're correct. Not sure why I added it to the list. :stuck_out_tongue:
ClassSource CompositeTestSource DirectorySource FilePosition FileSource MethodSource PackageSourceFor these I think it might make sense to use static factory methods. @junit-team/junit-lambda Thoughts?
I'm OK with making that change.
... by which I mean: introducing from(...) SFMs in concrete TestSource implementations.
ExpectedExceptionSupport (?) ExternalResourceSupport (?) VerifierSupport (?)These are instantiated by JUnit 4 and thus need a public constructor.
Well, _technically_ only the JUnitPlatform is instantiated by JUnit 4. The others are instantiated by JUnit Jupiter and therefore only require a no-args constructor (which need not be public).
But I nonetheless agree that we cannot introduce SFMs for those. 😉
SummaryGeneratingListenerThese do not declare a constructor and I don't foresee us adding one so they should be fine.
Agreed.
Has a single constructor that looks like in other libraries with similar helper classes. Thus, I'd rather keep it that way. It's already a builder, so if we ever needed to add an additional parameter we could add a builder method.
Agreed.
I think this one should definitely be changed because it uses both static factory methods and has a public constructor.
Agreed.
We should make the existing constructor private and introduce a new forBiConsumer(...) SFM.
_in progress_
Updated _Deliverables_.
FYI: current work can be viewed in the following branch.
https://github.com/junit-team/junit5/tree/issues/939-static-factory-methods
Update:
All changes are complete and have been pushed to the aforementioned branch for review.
Update:
Discussion should take place in PR #1046 instead of on the branch.
Closing this issue since #1046 has been merged into master.
@sbrannen @marcphilipp The results of #1046 LGTM now. Many thanks for taking the time and effort to address this! 😀