JUnit 5 extension for AemContext

Description

JUnit 5 introduced extensions model which can be very useful in tests using AemContext. With JUnit 4 rules model, if we want to use JCR_OAK, then it is created for all tests in class even if other methods doesn't need it or lighter resource resolver is enough. I noticed that in my current project we had classes where eg. 30 test methods required RESOURCERESOLVER_MOCK and JCR_OAK was actually needed by only 1 test. Test class was executed for 15 seconds. After creating AemContext extension, we are able to inject proper context on methods level. eg:

@Test void test1(ResourceResolverContext context) { // fast test } @Test void test2(JcrOakContext context) { // heavy and slow test } @Test void test3() { // does not require context at all }

In such approach test class execution changed from 15 seconds to ~1 second what significantly speeds up build time. I'm still talking about single test class.

If you are interested in adding it to your project I can create pull request on GitHub with this feature.

Gliffy Diagrams

Activity

Show:

Stefan Seifert August 13, 2018 at 3:40 PM

implementation finished, can be tested as snapshot

Stefan Seifert August 3, 2018 at 2:29 PM

i've completed the splitup in core, junit4 and junit5 subprojects and added a relocator pom to ease upgrade from projects using the old versions, and fixed some remaining issue.
for support junit5 in osgi-mock and sling-mock i've created SLING-7803.

Stefan Seifert July 13, 2018 at 3:01 PM

status update:

  • i did some work on the branch this week and completed the missing functionality. now the same features are supported as in junit 4.

  • the only feature i dropped is the possibility to pass in multiple resource resolver types to be executed the tests in all of them - i assume this features was only used in the aem mock unit tests itself. within a junit 5 extension it's currently not possible to implement it, also it's not possible to use parameterized tests for this because they are not supported on class level (#878). maybe we can add this feature again with junit 5.3 or later.

  • the feature to pass in AemContext or one of it's resource-resolver type specific subclasses showed up to be very limited - if found now easy/clean way to to support context plugins and the other customizations possible via AemContextBuilder for them. so they are only usable for the simple use cases. but the AemContextExtension also supports the traditional way having the AemContext as member variable, and with this way it's possible to use AemContextBuilder with all customizations quite the same way as in Junit 4.

  • supporting both junit 4 and junit 5 in the same artifact is possible, but not feasible. i dropped the idea to embed some classes from junit 4 to extend from the junit 4 implementation to avoid defining two AemContext classes for each junit version. on the one hand it would drag in some more dependend classes creating more a hack than a solution, on the other hand junit is published unter EPL license which is not allowed as source inclusion with apache license.

  • so my proposal is to create two artifacts aem-mock.junit4 and aem-mock.junit5, each including only the dependencies required and not polluting the dependency list with unneeded dependencies - and avoiding to have two similar AemContext classes in the classpath.

next steps:

  • split up aem-mock in aem-mock.core, aem-mock.junit4, aem-mock.junit5

  • implement junit 5 support also in sling osgi-mock and sling-mock in the same way and update to them

  • update documentation

i plan to complete it in august.

Karol Lewandowski January 22, 2018 at 10:03 PM

For me it sounds reasonable to implement this approach. Avoiding hard to spot mistakes that developers will get into for sure convinces me. It would be also annoying to have 2 classes with the same name completed in IDEs.
Having 4 classes that probably won't change isn't that risky (at least TestRule's code was changed 7 years ago). Even if it will cause any errors in future, users will get it immediately and could revert to older version and create bug issue here.

Stefan Seifert January 12, 2018 at 1:02 PM

perfect! - this works very well.

with this we can not only support a AemContext field member on the class and inject a proper AemContext, we can also support a field that was already initialized in the test code (e.g. with special plugins, resource resolver type etc.) - in the same style as in the JUnit 4 rule. added some specialized test cases for this.

i'm wondering if we should drop the new variant of io.wcm.testing.mock.aem.junit5.AemContext altogether and reuse the already existing io.wcm.testing.mock.aem.junit.AemContext. this would have several benefits:

  • avoid using the wrong class of those two when doing auto-completion in your IDE (esp. as it fails subtly if the wrong one is used)

  • migration from junit 4 to junit 5 becomes even easier - just replace the @Rule annotation with @ExtendsWith annotation.

  • avoid code duplication between those two

  • we can still support the specialized classes like JcrMockAemContext - just as sublcasses of the old AemContext.

drawback of this would be:

  • we need to have at last 4 classes of junit 4 (e.g. TestRule) in the classpath because they are references in the old AemContext implementation. but these 4 classes are quite standalone and have no references to other classes - we could include them in the jar - it's very unlikely the will be changed in the future. at all cost i want to avoid keeping a compile dependency to junit 4 in aem mocks - so projects using only junit 5 are not automatically polluted with the junit 4 classes (again for making code completion in IDEs fail safe)

Fixed

Details

Assignee

Reporter

Components

Fix versions

Priority

Created November 15, 2017 at 11:48 AM
Updated August 30, 2018 at 1:35 PM
Resolved August 13, 2018 at 3:40 PM