From 5d1a5e1da4bdb35f9f5add65d7e437a194702005 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Sun, 16 Oct 2022 21:54:15 -0500 Subject: [PATCH 01/22] Initial work --- .../andhow/testutil/AndHowTestUtils.java | 26 ++- .../andhow/junit5/ext/ExtensionBase.java | 2 +- .../pom.xml | 16 +- .../andhow/junit5/ext/ConfigFromFileExt.java | 162 ++++++++++++++++++ .../junit5/ext/ConfigFromFileExtTest.java | 152 ++++++++++++++++ 5 files changed, 339 insertions(+), 19 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index 4ffa9709..abca573c 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -163,17 +163,27 @@ public static T setAndHowInitialization(T newInit) { } /** - * Set a locator to find AndHowConfiguration. - * - * The locator is used in AndHow.findConfig(). If no config exists, the normal path is - * for AndHow to call {@code AndHowUtil.findConfiguration(c)}, however, if a locator - * is set to nonnull, it will be used instead. + * Specify the Locator used to find a AndHowConfiguration instance. + *

+ * This can be used for testing when you want to carefully control how AndHow + * is configured and do not want AndHow to auto-locate an AndHowConfiguration + * class from the classpath. + *

+ * The locator is used when the AndHow.findConfig() method is used to find + * AndHow's configuration. If no AndHowConfiguration instance has been + * discovered up to this point in the AndHow lifecycle (or if AndHow has been + * reset to it's unconfigured state for the purposes of a test), AndHow will + * use the Locator to find an AndHowConfiguration instance. If the Locator is + * null, a default classpath search is used. *

- * The locator takes a default Configuration to return if a configuration cannot - * be found otherwise. See org.yarnandtail.AndHow#findConfig() for details. + * The Locator is a UnaryOperator that takes a default AndHowConfiguration + * instance as a default instance to return if the locator cannot find an + * instance otherwise. The Locator can choose to ignore the default and + * simply return a hard-coded AndHowConfiguration instance (which is typically + * what happens for testing). *

* Example setting to a custom locator: - *

{@code setAndHowConfigLocator(c -> return MyConfig); }
+ *
{@code setAndHowConfigLocator( (c) -> new MyAndHowConfig() ) }
* Example setting back to null: *
{@code setAndHowConfigLocator(null); }
*

diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java index 218d47a9..340a6eee 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java @@ -13,7 +13,7 @@ public class ExtensionBase { *

  • The test class instance that is invoking this extension
  • * * - * This method should not be called for storate + retrieval related to a test method, since + * This method should not be called for storage + retrieval related to a test method, since * it will not be unique enough (other methods could overwrite its value). * * @param context The ExtensionContext passed in to one of the callback methods. diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml b/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml index 87e47bbd..2dbe2492 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml @@ -28,17 +28,21 @@ NOTE: Any dependency added to this list that would be needed by a user of the module must also be added as a dep' to andhow-junit5-extensions, which bundles this into a jar. --> + + org.yarnandtail + andhow-core + ${project.version} + true + ${project.groupId} andhow-shared-test-utils ${project.version} - compile ${project.groupId} junit5-extensions-no-andhow-dependency ${project.version} - compile true @@ -66,14 +70,6 @@ logback-classic true - - - - org.yarnandtail - andhow-core - ${project.version} - test - diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java new file mode 100644 index 00000000..d876e923 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -0,0 +1,162 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.*; +import org.yarnandtail.andhow.api.StandardLoader; +import org.yarnandtail.andhow.load.std.*; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import java.util.List; +import java.util.function.UnaryOperator; + +public class ConfigFromFileExt extends ExtensionBase + implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { + + /** The complete path to a properties file on the classpath */ + private String _classpathFile; + + /** + * New instance - validation of the classpathFile is deferred until use. + * @param classpathFile Complete path to a properties file on the classpath + */ + public ConfigFromFileExt(String classpathFile) { + _classpathFile = classpathFile; + } + + protected static final String CORE_KEY = "core_key"; + protected static final String LOCATOR_KEY = "locator_key"; + + /** + * Configure AndHow for a unit test class. + *

    + * This does the following: + *

      + *
    • Store the state of AndHow and the configuration locator + *
    • Destroy the configured state of AndHow so it is unconfigured + *
    • Remove environment related loaders that would be unwanted during unit testing + *
    • Specify the classpath of a properties file to be used for configuration + * for this test, as spec'ed in the constructor to this class. + *
    + * + * @param context Passed by JUnit prior to any test in the class + * @throws Exception + */ + @Override + public void beforeAll(ExtensionContext context) throws Exception { + + // remove current core and keep to be later restored + getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); + + // New config instance created just as needed for testing + AndHowConfiguration config = buildConfig(_classpathFile); + + // Remove current locator and replace w/ one that always returns a custom config + getPerTestClassStore(context).put( + LOCATOR_KEY, + AndHowTestUtils.setAndHowConfigLocator((c) -> config) ); + + } + + @Override + public void afterAll(ExtensionContext context) throws Exception { + Object core = getPerTestClassStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); + AndHowTestUtils.setAndHowCore(core); + + UnaryOperator locator = getPerTestClassStore(context).remove(LOCATOR_KEY, UnaryOperator.class); + AndHowTestUtils.setAndHowConfigLocator(locator); + } + + + /** + * Store the state of AndHow before this test, then destroy the state so AndHow is unconfigured. + * @param context + * @throws Exception + */ + @Override + public void beforeEach(ExtensionContext context) throws Exception { + getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); + + // New config instance created just as needed for testing + AndHowConfiguration config = buildConfig(_classpathFile); + + // Remove current locator and replace w/ one that always returns a custom config + getPerTestMethodStore(context).put( + LOCATOR_KEY, + AndHowTestUtils.setAndHowConfigLocator((c) -> config) ); + } + + /** + * Restore the state of AndHow to what it was before this test. + * @param context + * @throws Exception + */ + @Override + public void afterEach(ExtensionContext context) throws Exception { + Object core = getPerTestMethodStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); + AndHowTestUtils.setAndHowCore(core); + + UnaryOperator locator = getPerTestMethodStore(context).remove(LOCATOR_KEY, UnaryOperator.class); + AndHowTestUtils.setAndHowConfigLocator(locator); + } + + + /** + * Remove the Loaders that are 'environment' related, meaning they could be set + * outside the scope of a unit test. + *

    + * These loaders are removed to prevent environment from affecting a test. + * + * @param config The AndHowConfiguration to remove the loaders from. + */ + protected void removeEnvLoaders(AndHowConfiguration config) { + + List> loaders = config.getDefaultLoaderList(); + loaders.remove(StdSysPropLoader.class); + loaders.remove(StdEnvVarLoader.class); + loaders.remove(StdJndiLoader.class); + + config.setStandardLoaders(loaders).setStandardLoaders(loaders); + } + + /** + * Construct a new AndHowConfiguration instance created as needed for testing + * @param classpathFile + * @return + */ + protected AndHowConfiguration buildConfig(String classpathFile) { + AndHowConfiguration config = new StdConfig.StdConfigImpl(); + removeEnvLoaders(config); + config.setClasspathPropFilePath(classpathFile); + + return config; + } + + /** + * Expand the passed classpath to be an absolute classpath if it was a relative one. + *

    + * A classpath is determined to be a relative path if it does not start with a slash. + * In that case, the package of the path is expanded to include the package of the test + * on which this extension is used. + * If the path contains a slash, but it does not start with a slash, it is an illegal argument. + * Other non-allowed characters in a single classpath are illegal, including spaces and commas. + * All paths are trimmed of whitespace before processing. + * Null paths will throw a NullPointer. + * @param classpath The classpath to the properties file the user wants to use to configure AndHow + * @param context The test context this extension is being run within + * @return + */ + protected String expandPath(String classpath, ExtensionContext context) { + String fullPath = classpath.trim(); + + if (! fullPath.startsWith("/")) { + String pkgName = context.getRequiredTestClass().getPackageName(); + String pkgPath = pkgName.replace(".", "/"); + if (pkgPath.length() > 0) pkgPath = "/" + pkgPath; + + fullPath = pkgPath + "/" + fullPath; + } + + return fullPath; + } +} + diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java new file mode 100644 index 00000000..9298e250 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java @@ -0,0 +1,152 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.*; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; + +class ConfigFromFileExtTest { + + Object andHowCoreCreatedDuringTest; + + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace; + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() { + + // Setup AndHow for the test + assertNull(AndHowTestUtils.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHowTestUtils.invokeAndHowInstance(); //force creation + + andHowCoreCreatedDuringTest = AndHowTestUtils.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + store = Mockito.mock(ExtensionContext.Store.class); + Mockito.when(store.remove(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(andHowCoreCreatedDuringTest); + + extensionContext = Mockito.mock(ExtensionContext.class); + Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); + Mockito.when(extensionContext.getStore(ArgumentMatchers.any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowTestUtils.setAndHowCore(null); + } + + @Test + public void expandPathShouldExpandRelativePaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", + ext.expandPath("myFile.props", extensionContext)); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/sub/myFile.props", + ext.expandPath("sub/myFile.props", extensionContext)); + + //Need to test a class at the root somehow (pkg is empty) + } + + @Test + public void expandPathShouldNotExpandAbsPaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/myFile.props", + ext.expandPath("/myFile.props", extensionContext)); + + assertEquals("/myFile", + ext.expandPath("/myFile", extensionContext)); + + assertEquals("/sub/myFile.props", + ext.expandPath("/sub/myFile.props", extensionContext)); + + assertEquals("/sub/myFile", + ext.expandPath("/sub/myFile", extensionContext)); + + //Need to test a class at the root somehow (pkg is empty) + } + + @Test + void completeWorkflow() throws Exception { + + String cp = "my_file.properties"; + + ConfigFromFileExt theExt = new ConfigFromFileExt(cp); + + // The initial event called on extension by JUnit + theExt.beforeAll(extensionContext); + + assertNull(AndHowTestUtils.getAndHowCore(), + "Extension should have killed the core"); + + // The final event called on the extension by Junit + theExt.afterAll(extensionContext); + + // + // Verify the overall outcome + assertEquals(andHowCoreCreatedDuringTest, AndHowTestUtils.getAndHowCore(), + "Extension should have reinstated the same core instance created in setup"); + + + // + // Verify actions on the store + ArgumentCaptor keyForPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForRemove = ArgumentCaptor.forClass(Object.class); + + InOrder orderedStoreCalls = Mockito.inOrder(store); + orderedStoreCalls.verify(store).put(keyForPut.capture(), ArgumentMatchers.eq(andHowCoreCreatedDuringTest)); + orderedStoreCalls.verify(store).remove(keyForRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); + Mockito.verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + assertEquals(keyForPut.getValue(), keyForRemove.getValue(), + "The keys used for put & remove should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + //Each method is called once in beforeAll and afterAll + Mockito.verify(extensionContext, Mockito.times(2)).getRequiredTestClass(); //Must be called to figure out the Test class + Mockito.verify(extensionContext, Mockito.times(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class and the + // Tested class (this class in this case). There isn't an easy way to test that minimum spec, + // so here just check for a specific namespace. + expectedNamespace = ExtensionContext.Namespace.create( + KillAndHowBeforeAllTestsExt.class, + (Class)(this.getClass())); + assertEquals(expectedNamespace, namespace.getValue()); + + } + + /* Simple subclass to test protected methods */ + public static class ConfigFromFileExtSimple extends ConfigFromFileExt { + public ConfigFromFileExtSimple() { + super(""); + } + + public String expandPath(String classpath, ExtensionContext context) { + return super.expandPath(classpath, context); + } + } + + +} \ No newline at end of file From f4f3066197676cb178b8a993ccfa197747bb711c Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 17 Oct 2022 09:21:16 -0500 Subject: [PATCH 02/22] Improved testing and some fixes - tests not fully passing yet --- .../andhow/junit5/ext/ConfigFromFileExt.java | 6 +- .../ConfigFromFileExtDefaultPackageTest.java | 102 ++++++++++++++++++ .../src/test/java/MyClassWithNoPackage.java | 5 + .../junit5/ext/ConfigFromFileExtTest.java | 14 +++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/MyClassWithNoPackage.java diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index d876e923..30b5a9aa 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -48,7 +48,8 @@ public void beforeAll(ExtensionContext context) throws Exception { getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); // New config instance created just as needed for testing - AndHowConfiguration config = buildConfig(_classpathFile); + AndHowConfiguration config = + buildConfig(expandPath(_classpathFile, context)); // Remove current locator and replace w/ one that always returns a custom config getPerTestClassStore(context).put( @@ -77,7 +78,8 @@ public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); // New config instance created just as needed for testing - AndHowConfiguration config = buildConfig(_classpathFile); + AndHowConfiguration config = + buildConfig(expandPath(_classpathFile, context)); // Remove current locator and replace w/ one that always returns a custom config getPerTestMethodStore(context).put( diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java new file mode 100644 index 00000000..96685a37 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java @@ -0,0 +1,102 @@ +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.*; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Duplicates some tests from org.yarnandtail.andhow.junit5.ConfigFromFileExtTest, + * but does it using a similated Test class that is in the default package. + * In newer JDKs it is not possible to import a class in the default package, + * so the only way to refer to such is a class is to also be in the default + * package, thus this test class. + */ +class ConfigFromFileExtDefaultPackageTest { + + Object andHowCoreCreatedDuringTest; + + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace; + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() { + + // Setup AndHow for the test + assertNull(AndHowTestUtils.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHowTestUtils.invokeAndHowInstance(); //force creation + + andHowCoreCreatedDuringTest = AndHowTestUtils.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + store = Mockito.mock(ExtensionContext.Store.class); + Mockito.when(store.remove(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(andHowCoreCreatedDuringTest); + + extensionContext = Mockito.mock(ExtensionContext.class); + Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(MyClassWithNoPackage.class)); + Mockito.when(extensionContext.getStore(ArgumentMatchers.any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowTestUtils.setAndHowCore(null); + } + + @Test + public void expandPathShouldExpandRelativePaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/myFile.props", + ext.expandPath("myFile.props", extensionContext)); + + assertEquals("/sub/myFile.props", + ext.expandPath("sub/myFile.props", extensionContext)); + + //Need to test a class at the root somehow (pkg is empty) + } + + @Test + public void expandPathShouldNotExpandAbsPaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/myFile.props", + ext.expandPath("/myFile.props", extensionContext)); + + assertEquals("/myFile", + ext.expandPath("/myFile", extensionContext)); + + assertEquals("/sub/myFile.props", + ext.expandPath("/sub/myFile.props", extensionContext)); + + assertEquals("/sub/myFile", + ext.expandPath("/sub/myFile", extensionContext)); + + //Need to test a class at the root somehow (pkg is empty) + } + + /* Simple subclass to test protected methods */ + public static class ConfigFromFileExtSimple extends ConfigFromFileExt { + public ConfigFromFileExtSimple() { + super(""); + } + + public String expandPath(String classpath, ExtensionContext context) { + return super.expandPath(classpath, context); + } + } + +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/MyClassWithNoPackage.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/MyClassWithNoPackage.java new file mode 100644 index 00000000..cb74097e --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/MyClassWithNoPackage.java @@ -0,0 +1,5 @@ +/** + * This class is used by some classes to test classpaths for classes with no package. + */ +public class MyClassWithNoPackage { +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java index 9298e250..4abfed8a 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java @@ -82,6 +82,20 @@ public void expandPathShouldNotExpandAbsPaths() { //Need to test a class at the root somehow (pkg is empty) } + @Test + public void expandPathShouldReturnPackageOfContainingClassForInnerClasses() { + //Set mock test class to an inner class + Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(ConfigFromFileExtSimple.class)); + + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", + ext.expandPath("myFile.props", extensionContext)); + } + + /* NOTE: Testing building correct paths with the default package are handled + by a separate test in the default package. */ + @Test void completeWorkflow() throws Exception { From 0ceddaab51465a684183f5ee4ee99924409e3b25 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 17 Oct 2022 22:01:07 -0500 Subject: [PATCH 03/22] Tests are passing, but need to test functionality better --- .../pom.xml | 5 ++ .../andhow/junit5/ext/ConfigFromFileExt.java | 2 +- .../junit5/ext/ConfigFromFileExtTest.java | 56 +++++++++++++++---- 3 files changed, 50 insertions(+), 13 deletions(-) diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml b/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml index 2dbe2492..6548d791 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/pom.xml @@ -38,6 +38,11 @@ ${project.groupId} andhow-shared-test-utils ${project.version} + + + ${project.groupId} + andhow-test-stubs + ${project.groupId} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index 30b5a9aa..590ef849 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -63,7 +63,7 @@ public void afterAll(ExtensionContext context) throws Exception { Object core = getPerTestClassStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); AndHowTestUtils.setAndHowCore(core); - UnaryOperator locator = getPerTestClassStore(context).remove(LOCATOR_KEY, UnaryOperator.class); + UnaryOperator locator = getPerTestClassStore(context).remove(LOCATOR_KEY, UnaryOperator.class); AndHowTestUtils.setAndHowConfigLocator(locator); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java index 4abfed8a..5a040782 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java @@ -2,10 +2,15 @@ import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.engine.execution.NamespaceAwareStore; import org.mockito.*; +import org.yarnandtail.andhow.AndHowConfiguration; import org.yarnandtail.andhow.testutil.AndHowTestUtils; +import java.util.function.UnaryOperator; + import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.times; class ConfigFromFileExtTest { @@ -38,7 +43,15 @@ void setUp() { // Setup mockito for the test store = Mockito.mock(ExtensionContext.Store.class); - Mockito.when(store.remove(ArgumentMatchers.any(), ArgumentMatchers.any())).thenReturn(andHowCoreCreatedDuringTest); + + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getCoreKey()), ArgumentMatchers.any()) + ).thenReturn(andHowCoreCreatedDuringTest); + + // NEED TO ALSO COVER CASES WHEN THIS IS NOT NULL (SAME FOR ABOVE BY THE REVERSE) + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getLocatorKey()), ArgumentMatchers.any()) + ).thenReturn(null); extensionContext = Mockito.mock(ExtensionContext.class); Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); @@ -120,32 +133,43 @@ void completeWorkflow() throws Exception { // // Verify actions on the store - ArgumentCaptor keyForPut = ArgumentCaptor.forClass(Object.class); - ArgumentCaptor keyForRemove = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForCorePut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForLocatorPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForCoreRemove = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForLocatorRemove = ArgumentCaptor.forClass(Object.class); + // In order, there should be 2x puts and 2x removes - can't be more specific on order than that. InOrder orderedStoreCalls = Mockito.inOrder(store); - orderedStoreCalls.verify(store).put(keyForPut.capture(), ArgumentMatchers.eq(andHowCoreCreatedDuringTest)); - orderedStoreCalls.verify(store).remove(keyForRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); + orderedStoreCalls.verify(store, times(2)).put(ArgumentMatchers.any(), ArgumentMatchers.any()); + orderedStoreCalls.verify(store, times(2)).remove(ArgumentMatchers.any(), ArgumentMatchers.any()); Mockito.verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store - assertEquals(keyForPut.getValue(), keyForRemove.getValue(), - "The keys used for put & remove should be the same"); + + // Now verify actual arguments to the store (can't assume in the order above) + Mockito.verify(store).put(keyForCorePut.capture(), ArgumentMatchers.eq(andHowCoreCreatedDuringTest)); + Mockito.verify(store).put(keyForLocatorPut.capture(), ArgumentMatchers.isNull()); // Initial locator was null, so getting it back here + Mockito.verify(store).remove(keyForCoreRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); + Mockito.verify(store).remove(keyForLocatorRemove.capture(), ArgumentMatchers.eq(UnaryOperator.class)); + + assertEquals(keyForCorePut.getValue(), keyForCoreRemove.getValue(), + "The keys used for put & remove the core should be the same"); + + assertEquals(keyForLocatorPut.getValue(), keyForLocatorRemove.getValue(), + "The keys used for put & remove the locator should be the same"); // // Verify actions on the ExtensionContext ArgumentCaptor namespace = ArgumentCaptor.forClass(ExtensionContext.Namespace.class); - - //Each method is called once in beforeAll and afterAll - Mockito.verify(extensionContext, Mockito.times(2)).getRequiredTestClass(); //Must be called to figure out the Test class - Mockito.verify(extensionContext, Mockito.times(2)).getStore(namespace.capture()); + + Mockito.verify(extensionContext, Mockito.atLeast(2)).getStore(namespace.capture()); //Verify the namespace used // The namespace is an implementation detail, but must include the Extension class and the // Tested class (this class in this case). There isn't an easy way to test that minimum spec, // so here just check for a specific namespace. expectedNamespace = ExtensionContext.Namespace.create( - KillAndHowBeforeAllTestsExt.class, + ConfigFromFileExt.class, (Class)(this.getClass())); assertEquals(expectedNamespace, namespace.getValue()); @@ -160,6 +184,14 @@ public ConfigFromFileExtSimple() { public String expandPath(String classpath, ExtensionContext context) { return super.expandPath(classpath, context); } + + public static String getCoreKey() { + return ConfigFromFileExt.CORE_KEY; + } + + public static String getLocatorKey() { + return ConfigFromFileExt.LOCATOR_KEY; + } } From 884412facac96108580c2879fa665f69aa0057f5 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 27 Oct 2022 09:42:59 -0500 Subject: [PATCH 04/22] Rearranged tests --- .../andhow/testutil/AndHowTestUtils.java | 2 +- .../andhow/junit5/ext/ExtensionBase.java | 12 +- .../andhow/junit5/ext/ExtensionBaseTest.java | 29 ++ .../andhow/junit5/ext/ConfigFromFileExt.java | 35 +- .../junit5/ext/ConfigFromFileExtTest.java | 198 ----------- .../junit5/ext/ConfigFromFileExtUnitTest.java | 318 ++++++++++++++++++ .../ext/ConfigFromFileExtUsageTest.java | 14 + 7 files changed, 394 insertions(+), 214 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index abca573c..0fa38aa3 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -115,7 +115,7 @@ public static Object getAndHowCore() { * This method is relatively safe for use in application testing and is used by the Junit * extensions and annotations to set configurations for individual tests. * - * Note: This method will fail is AndHow is uninitialized. + * Note: This method will fail if AndHow is uninitialized. ** * @param newCore The new core to assign to the AndHow singleton which may be null but must * be of type AndHowCore. diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java index 340a6eee..5cfc0829 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java @@ -20,7 +20,11 @@ public class ExtensionBase { * @return The store that can be used to store and retrieve values. */ protected ExtensionContext.Store getPerTestClassStore(ExtensionContext context) { - return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass())); + return context.getStore(getPerTestNamespace(context)); + } + + protected ExtensionContext.Namespace getPerTestNamespace(ExtensionContext context) { + return ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass()); } /** @@ -41,6 +45,10 @@ protected ExtensionContext.Store getPerTestClassStore(ExtensionContext context) * @return The store that can be used to store and retrieve values. */ protected ExtensionContext.Store getPerTestMethodStore(ExtensionContext context) { - return context.getStore(ExtensionContext.Namespace.create(getClass(), context.getRequiredTestInstance(), context.getRequiredTestMethod())); + return context.getStore(getPerTestMethodNamespace(context)); + } + + protected ExtensionContext.Namespace getPerTestMethodNamespace(ExtensionContext context) { + return ExtensionContext.Namespace.create(getClass(), context.getRequiredTestInstance(), context.getRequiredTestMethod()); } } diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java new file mode 100644 index 00000000..11c8db92 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java @@ -0,0 +1,29 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ExtensionBaseTest { + + @BeforeEach + void setUp() { + } + + @Test + void getPerTestClassStore() { + } + + @Test + void getPerTestNamespace() { + } + + @Test + void getPerTestMethodStore() { + } + + @Test + void getPerTestMethodNamespace() { + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index 590ef849..46da7f54 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -15,6 +15,9 @@ public class ConfigFromFileExt extends ExtensionBase /** The complete path to a properties file on the classpath */ private String _classpathFile; + /** The constructed config instance to be used for AndHow */ + protected AndHowConfiguration _config; + /** * New instance - validation of the classpathFile is deferred until use. * @param classpathFile Complete path to a properties file on the classpath @@ -23,8 +26,14 @@ public ConfigFromFileExt(String classpathFile) { _classpathFile = classpathFile; } + /** Key to store the AndHowCore (if any) of AndHow. */ protected static final String CORE_KEY = "core_key"; - protected static final String LOCATOR_KEY = "locator_key"; + + /** Key to store the in-process configuration (if any) of the AndHow instance. + * When is the inProcessConfig non-null for AndHow? Only when findConfig() + * has been called but AndHow has not yet initialized. + */ + protected static final String CONFIG_KEY = "config_key"; /** * Configure AndHow for a unit test class. @@ -48,13 +57,12 @@ public void beforeAll(ExtensionContext context) throws Exception { getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); // New config instance created just as needed for testing - AndHowConfiguration config = - buildConfig(expandPath(_classpathFile, context)); + _config = buildConfig(expandPath(_classpathFile, context)); // Remove current locator and replace w/ one that always returns a custom config getPerTestClassStore(context).put( - LOCATOR_KEY, - AndHowTestUtils.setAndHowConfigLocator((c) -> config) ); + CONFIG_KEY, + AndHowTestUtils.setAndHowInProcessConfig(_config)); } @@ -63,8 +71,9 @@ public void afterAll(ExtensionContext context) throws Exception { Object core = getPerTestClassStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); AndHowTestUtils.setAndHowCore(core); - UnaryOperator locator = getPerTestClassStore(context).remove(LOCATOR_KEY, UnaryOperator.class); - AndHowTestUtils.setAndHowConfigLocator(locator); + AndHowConfiguration config = + getPerTestClassStore(context).remove(CONFIG_KEY, AndHowConfiguration.class); + AndHowTestUtils.setAndHowInProcessConfig(config); } @@ -78,13 +87,12 @@ public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); // New config instance created just as needed for testing - AndHowConfiguration config = - buildConfig(expandPath(_classpathFile, context)); + _config = buildConfig(expandPath(_classpathFile, context)); // Remove current locator and replace w/ one that always returns a custom config getPerTestMethodStore(context).put( - LOCATOR_KEY, - AndHowTestUtils.setAndHowConfigLocator((c) -> config) ); + CONFIG_KEY, + AndHowTestUtils.setAndHowInProcessConfig(_config)); } /** @@ -97,8 +105,9 @@ public void afterEach(ExtensionContext context) throws Exception { Object core = getPerTestMethodStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); AndHowTestUtils.setAndHowCore(core); - UnaryOperator locator = getPerTestMethodStore(context).remove(LOCATOR_KEY, UnaryOperator.class); - AndHowTestUtils.setAndHowConfigLocator(locator); + AndHowConfiguration config = + getPerTestMethodStore(context).remove(CONFIG_KEY, AndHowConfiguration.class); + AndHowTestUtils.setAndHowInProcessConfig(config); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java deleted file mode 100644 index 5a040782..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtTest.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.yarnandtail.andhow.junit5.ext; - -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.engine.execution.NamespaceAwareStore; -import org.mockito.*; -import org.yarnandtail.andhow.AndHowConfiguration; -import org.yarnandtail.andhow.testutil.AndHowTestUtils; - -import java.util.function.UnaryOperator; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.times; - -class ConfigFromFileExtTest { - - Object andHowCoreCreatedDuringTest; - - //The expected namespace used to store values within the Extension - ExtensionContext.Namespace expectedNamespace; - - //Store for state variables within the context - ExtensionContext.Store store; - - //The context object that is passed to the test extension - ExtensionContext extensionContext; - - - @BeforeEach - void setUp() { - - // Setup AndHow for the test - assertNull(AndHowTestUtils.getAndHowCore(), - "Just checking - no test should leave AndHow initialized"); - - AndHowTestUtils.invokeAndHowInstance(); //force creation - - andHowCoreCreatedDuringTest = AndHowTestUtils.getAndHowCore(); - - assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); - - // - // Setup mockito for the test - - store = Mockito.mock(ExtensionContext.Store.class); - - Mockito.when(store.remove(ArgumentMatchers.matches( - ConfigFromFileExtSimple.getCoreKey()), ArgumentMatchers.any()) - ).thenReturn(andHowCoreCreatedDuringTest); - - // NEED TO ALSO COVER CASES WHEN THIS IS NOT NULL (SAME FOR ABOVE BY THE REVERSE) - Mockito.when(store.remove(ArgumentMatchers.matches( - ConfigFromFileExtSimple.getLocatorKey()), ArgumentMatchers.any()) - ).thenReturn(null); - - extensionContext = Mockito.mock(ExtensionContext.class); - Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); - Mockito.when(extensionContext.getStore(ArgumentMatchers.any())).thenReturn(store); - } - - @AfterEach - void tearDown() { - AndHowTestUtils.setAndHowCore(null); - } - - @Test - public void expandPathShouldExpandRelativePaths() { - ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); - - assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", - ext.expandPath("myFile.props", extensionContext)); - - assertEquals("/org/yarnandtail/andhow/junit5/ext/sub/myFile.props", - ext.expandPath("sub/myFile.props", extensionContext)); - - //Need to test a class at the root somehow (pkg is empty) - } - - @Test - public void expandPathShouldNotExpandAbsPaths() { - ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); - - assertEquals("/myFile.props", - ext.expandPath("/myFile.props", extensionContext)); - - assertEquals("/myFile", - ext.expandPath("/myFile", extensionContext)); - - assertEquals("/sub/myFile.props", - ext.expandPath("/sub/myFile.props", extensionContext)); - - assertEquals("/sub/myFile", - ext.expandPath("/sub/myFile", extensionContext)); - - //Need to test a class at the root somehow (pkg is empty) - } - - @Test - public void expandPathShouldReturnPackageOfContainingClassForInnerClasses() { - //Set mock test class to an inner class - Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(ConfigFromFileExtSimple.class)); - - ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); - - assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", - ext.expandPath("myFile.props", extensionContext)); - } - - /* NOTE: Testing building correct paths with the default package are handled - by a separate test in the default package. */ - - @Test - void completeWorkflow() throws Exception { - - String cp = "my_file.properties"; - - ConfigFromFileExt theExt = new ConfigFromFileExt(cp); - - // The initial event called on extension by JUnit - theExt.beforeAll(extensionContext); - - assertNull(AndHowTestUtils.getAndHowCore(), - "Extension should have killed the core"); - - // The final event called on the extension by Junit - theExt.afterAll(extensionContext); - - // - // Verify the overall outcome - assertEquals(andHowCoreCreatedDuringTest, AndHowTestUtils.getAndHowCore(), - "Extension should have reinstated the same core instance created in setup"); - - - // - // Verify actions on the store - ArgumentCaptor keyForCorePut = ArgumentCaptor.forClass(Object.class); - ArgumentCaptor keyForLocatorPut = ArgumentCaptor.forClass(Object.class); - ArgumentCaptor keyForCoreRemove = ArgumentCaptor.forClass(Object.class); - ArgumentCaptor keyForLocatorRemove = ArgumentCaptor.forClass(Object.class); - - // In order, there should be 2x puts and 2x removes - can't be more specific on order than that. - InOrder orderedStoreCalls = Mockito.inOrder(store); - orderedStoreCalls.verify(store, times(2)).put(ArgumentMatchers.any(), ArgumentMatchers.any()); - orderedStoreCalls.verify(store, times(2)).remove(ArgumentMatchers.any(), ArgumentMatchers.any()); - Mockito.verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store - - - // Now verify actual arguments to the store (can't assume in the order above) - Mockito.verify(store).put(keyForCorePut.capture(), ArgumentMatchers.eq(andHowCoreCreatedDuringTest)); - Mockito.verify(store).put(keyForLocatorPut.capture(), ArgumentMatchers.isNull()); // Initial locator was null, so getting it back here - Mockito.verify(store).remove(keyForCoreRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); - Mockito.verify(store).remove(keyForLocatorRemove.capture(), ArgumentMatchers.eq(UnaryOperator.class)); - - assertEquals(keyForCorePut.getValue(), keyForCoreRemove.getValue(), - "The keys used for put & remove the core should be the same"); - - assertEquals(keyForLocatorPut.getValue(), keyForLocatorRemove.getValue(), - "The keys used for put & remove the locator should be the same"); - - // - // Verify actions on the ExtensionContext - ArgumentCaptor namespace = - ArgumentCaptor.forClass(ExtensionContext.Namespace.class); - - Mockito.verify(extensionContext, Mockito.atLeast(2)).getStore(namespace.capture()); - - //Verify the namespace used - // The namespace is an implementation detail, but must include the Extension class and the - // Tested class (this class in this case). There isn't an easy way to test that minimum spec, - // so here just check for a specific namespace. - expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, - (Class)(this.getClass())); - assertEquals(expectedNamespace, namespace.getValue()); - - } - - /* Simple subclass to test protected methods */ - public static class ConfigFromFileExtSimple extends ConfigFromFileExt { - public ConfigFromFileExtSimple() { - super(""); - } - - public String expandPath(String classpath, ExtensionContext context) { - return super.expandPath(classpath, context); - } - - public static String getCoreKey() { - return ConfigFromFileExt.CORE_KEY; - } - - public static String getLocatorKey() { - return ConfigFromFileExt.LOCATOR_KEY; - } - } - - -} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java new file mode 100644 index 00000000..df829033 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java @@ -0,0 +1,318 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.*; +import org.yarnandtail.andhow.*; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.times; + +class ConfigFromFileExtUnitTest { + + //Store for state variables within the context + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + + + @BeforeEach + void setUp() { + + // + // Setup mockito for the test + + store = Mockito.mock(ExtensionContext.Store.class); + + extensionContext = Mockito.mock(ExtensionContext.class); + Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); + Mockito.when(extensionContext.getStore(ArgumentMatchers.any())).thenReturn(store); + } + + @AfterEach + void tearDown() { + AndHowTestUtils.setAndHowCore(null); + } + + @Test + public void expandPathShouldExpandRelativePaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", + ext.expandPath("myFile.props", extensionContext)); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/sub/myFile.props", + ext.expandPath("sub/myFile.props", extensionContext)); + + + //Need to test a class at the root somehow (pkg is empty) + } + + @Test + public void expandPathShouldNotExpandAbsPaths() { + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/myFile.props", + ext.expandPath("/myFile.props", extensionContext)); + + assertEquals("/myFile", + ext.expandPath("/myFile", extensionContext)); + + assertEquals("/sub/myFile.props", + ext.expandPath("/sub/myFile.props", extensionContext)); + + assertEquals("/sub/myFile", + ext.expandPath("/sub/myFile", extensionContext)); + + //Need to test a class at the root somehow (pkg is empty) + } + + @Test + public void expandPathShouldReturnPackageOfContainingClassForInnerClasses() { + //Set mock test class to an inner class + Mockito.when(extensionContext.getRequiredTestClass()) + .thenReturn((Class)(ConfigFromFileExtSimple.class)); + + ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", + ext.expandPath("myFile.props", extensionContext)); + } + + /* NOTE: Testing building correct paths with the default package are handled + by a separate test in the default package. */ + + /** + * This test scenario (non-null core and null inProcessConfig) is the case + * when AndHow has been initialized. + * @throws Exception + */ + @Test + void completeWorkflowWithNonNullCoreAndNullConfig() throws Exception { + + Object andHowCoreCreatedDuringTest; + + /// Setup Mocks /// + /// AndHow will be initialized (non-null core), the locator will be null + assertNull(AndHowTestUtils.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHowTestUtils.invokeAndHowInstance(); //force creation + + andHowCoreCreatedDuringTest = AndHowTestUtils.getAndHowCore(); + + assertNotNull(andHowCoreCreatedDuringTest, "Should be non-null because we forced creation"); + + // + // Setup mockito for the test + + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getCoreKey()), ArgumentMatchers.any()) + ).thenReturn(andHowCoreCreatedDuringTest); + + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getConfigKey()), ArgumentMatchers.any()) + ).thenReturn(null); + + /// /// /// /// + + String cp = "my_file.properties"; + + ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); + + // The initial event called on extension by JUnit + theExt.beforeAll(extensionContext); + + assertNull(AndHowTestUtils.getAndHowCore(), + "Extension should have killed the core"); + + assertSame(theExt.getCreatedConfig(), AndHow.findConfig()); + + // TODO: This test doesn't actually test that the created Core is as we want it. + + // The final event called on the extension by Junit + theExt.afterAll(extensionContext); + + // + // Verify the overall outcome + assertEquals(andHowCoreCreatedDuringTest, AndHowTestUtils.getAndHowCore(), + "Extension should have reinstated the same core instance created in setup"); + + + // + // Verify actions on the store + ArgumentCaptor keyForCorePut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForConfigPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForCoreRemove = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForConfigRemove = ArgumentCaptor.forClass(Object.class); + + // In order, there should be 2x puts and 2x removes - can't be more specific on order than that. + InOrder orderedStoreCalls = Mockito.inOrder(store); + orderedStoreCalls.verify(store, times(2)).put(ArgumentMatchers.any(), ArgumentMatchers.any()); + orderedStoreCalls.verify(store, times(2)).remove(ArgumentMatchers.any(), ArgumentMatchers.any()); + Mockito.verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + + // Now verify actual arguments to the store (can't assume in the order above) + Mockito.verify(store).put(keyForCorePut.capture(), ArgumentMatchers.eq(andHowCoreCreatedDuringTest)); + Mockito.verify(store).put(keyForConfigPut.capture(), ArgumentMatchers.isNull()); // Initial config was null, so getting it back here + Mockito.verify(store).remove(keyForCoreRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); + Mockito.verify(store).remove(keyForConfigRemove.capture(), ArgumentMatchers.eq(AndHowConfiguration.class)); + + assertEquals(keyForCorePut.getValue(), keyForCoreRemove.getValue(), + "The keys used for put & remove the core should be the same"); + + assertEquals(keyForConfigPut.getValue(), keyForConfigRemove.getValue(), + "The keys used for put & remove the locator should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + Mockito.verify(extensionContext, Mockito.atLeast(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class and the + // Tested class (this class in this case). There isn't an easy way to test that minimum spec, + // so here just check for a specific namespace. + //The expected namespace used to store values within the Extension + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExtSimple.class, + (Class)(this.getClass())); + assertEquals(expectedNamespace, namespace.getValue()); + + } + + + /** + * This scenario (null core and non-null config) is the case when findConfig() has been called + * but AndHow has not yet been initialized. + * @throws Exception + */ + @Test + void completeWorkflowWithNullCoreAndNonNullConfig() throws Exception { + + /// Setup Mocks /// + /// AndHow will be un-initialized (null core), the locator will be non-null + assertNull(AndHowTestUtils.getAndHowCore(), + "Just checking - no test should leave AndHow initialized"); + + AndHowConfiguration existingConfig = AndHow.findConfig(); + assertNotNull(existingConfig); + + // + // Setup mockito for the test + AndHowConfiguration newConfig = StdConfig.instance(); + + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getCoreKey()), ArgumentMatchers.any()) + ).thenReturn(null); + + Mockito.when(store.remove(ArgumentMatchers.matches( + ConfigFromFileExtSimple.getConfigKey()), ArgumentMatchers.any()) + ).thenReturn(existingConfig); + + /// /// /// /// + + + String cp = "my_file.properties"; + + ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); + + // The initial event called on extension by JUnit + theExt.beforeAll(extensionContext); + + assertNull(AndHowTestUtils.getAndHowCore(), + "Should still be uninitialized"); + + assertSame(theExt.getCreatedConfig(), AndHow.findConfig()); + + + // The final event called on the extension by Junit + theExt.afterAll(extensionContext); + + // + // Verify the overall outcome + assertNull(AndHowTestUtils.getAndHowCore(), + "Extension should be null after the test"); + + + // + // Verify actions on the store + ArgumentCaptor keyForCorePut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForConfigPut = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForCoreRemove = ArgumentCaptor.forClass(Object.class); + ArgumentCaptor keyForConfigRemove = ArgumentCaptor.forClass(Object.class); + + // In order, there should be 2x puts and 2x removes - can't be more specific on order than that. + InOrder orderedStoreCalls = Mockito.inOrder(store); + orderedStoreCalls.verify(store, times(2)).put(ArgumentMatchers.any(), ArgumentMatchers.any()); + orderedStoreCalls.verify(store, times(2)).remove(ArgumentMatchers.any(), ArgumentMatchers.any()); + Mockito.verifyNoMoreInteractions(store); //Really don't want any other interaction w/ the store + + + // Now verify actual arguments to the store (can't assume in the order above) + Mockito.verify(store).put(keyForCorePut.capture(), ArgumentMatchers.isNull()); + Mockito.verify(store).put(keyForConfigPut.capture(), ArgumentMatchers.eq(existingConfig)); // Initial config AndHow had + Mockito.verify(store).remove(keyForCoreRemove.capture(), ArgumentMatchers.eq(AndHowTestUtils.getAndHowCoreClass())); + Mockito.verify(store).remove(keyForConfigRemove.capture(), ArgumentMatchers.eq(AndHowConfiguration.class)); + + assertEquals(keyForCorePut.getValue(), keyForCoreRemove.getValue(), + "The keys used for put & remove the core should be the same"); + + assertEquals(keyForConfigPut.getValue(), keyForConfigRemove.getValue(), + "The keys used for put & remove the locator should be the same"); + + // + // Verify actions on the ExtensionContext + ArgumentCaptor namespace = + ArgumentCaptor.forClass(ExtensionContext.Namespace.class); + + Mockito.verify(extensionContext, Mockito.atLeast(2)).getStore(namespace.capture()); + + //Verify the namespace used + // The namespace is an implementation detail, but must include the Extension class and the + // Tested class (this class in this case). There isn't an easy way to test that minimum spec, + // so here just check for a specific namespace. + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExtSimple.class, + (Class)(this.getClass())); + assertEquals(expectedNamespace, namespace.getValue()); + + } + + /* Simple subclass to test protected methods */ + public static class ConfigFromFileExtSimple extends ConfigFromFileExt { + + public ConfigFromFileExtSimple() { + super(""); + } + + public ConfigFromFileExtSimple(String classpathFile) { + super(classpathFile); + } + + public static String getCoreKey() { + return ConfigFromFileExt.CORE_KEY; + } + + public static String getConfigKey() { + return ConfigFromFileExt.CONFIG_KEY; + } + + public String expandPath(String classpath, ExtensionContext context) { + return super.expandPath(classpath, context); + } + + public AndHowConfiguration getCreatedConfig() { + return _config; + } + + + } + + +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java new file mode 100644 index 00000000..7cae058c --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java @@ -0,0 +1,14 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.*; +import org.yarnandtail.andhow.*; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.times; + +class ConfigFromFileExtUsageTest { + +} \ No newline at end of file From e6bf2ab65bf8e5a8918e2b040074bbc068c75f13 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Fri, 28 Oct 2022 11:33:33 -0500 Subject: [PATCH 05/22] Complete usage test for ConfigFromFileExt * Switch to newer JUnit and now using JUnit BOM pom * Added override groups to the AndHowTestUtils class * Now detecting null or invalid classpath path for ConfigFromFileExt --- .../andhow/testutil/AndHowTestUtils.java | 14 +- .../andhow/junit5/ext/ConfigFromFileExt.java | 26 ++- .../junit5/ext/ConfigFromFileExtUnitTest.java | 4 +- .../ext/ConfigFromFileExtUsageTest.java | 180 +++++++++++++++++- .../andhow/junit5/ext/MyPropFile.properties | 1 + .../andhow/junit5/ext/MyPropFile2.properties | 1 + .../andhow/junit5/ext/MyPropFile3.properties | 1 + .../andhow/junit5/ext/MyPropFile4.properties | 1 + pom.xml | 30 +-- 9 files changed, 228 insertions(+), 30 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index 0fa38aa3..66316235 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -1,5 +1,6 @@ package org.yarnandtail.andhow.testutil; +import java.util.List; import java.util.function.UnaryOperator; /** @@ -202,6 +203,18 @@ public static UnaryOperator setAndHowConfigLocator(UnaryOperator newLo getAndHowClass(), "configLocator", newLocator); } + public static List> setConfigurationOverrideGroups( + Object configurationInstance, List> classList) { + + return ReflectionTestUtils.setInstanceFieldValue( + configurationInstance, "overrideGroups", classList, List.class); + } + + public static List> setConfigurationOverrideGroups( + Object configurationInstance, Class clazz) { + return setConfigurationOverrideGroups(configurationInstance, List.of(clazz)); + } + /** * Forces the AndHow inProcessConfig to a new value. * @@ -284,5 +297,4 @@ private static Class getAndHowInitializationClass() { public static Class getAndHowConfigurationClass() { return ReflectionTestUtils.getClassByName("org.yarnandtail.andhow.AndHowConfiguration"); } - } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index 46da7f54..9f264835 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -23,6 +23,9 @@ public class ConfigFromFileExt extends ExtensionBase * @param classpathFile Complete path to a properties file on the classpath */ public ConfigFromFileExt(String classpathFile) { + if (classpathFile == null) { + throw new IllegalArgumentException("The classpath properties file path cannot be null."); + } _classpathFile = classpathFile; } @@ -56,8 +59,11 @@ public void beforeAll(ExtensionContext context) throws Exception { // remove current core and keep to be later restored getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); + String fullPath = expandPath(_classpathFile, context); + verifyClassPath(fullPath); + // New config instance created just as needed for testing - _config = buildConfig(expandPath(_classpathFile, context)); + _config = buildConfig(fullPath); // Remove current locator and replace w/ one that always returns a custom config getPerTestClassStore(context).put( @@ -86,8 +92,11 @@ public void afterAll(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); + String fullPath = expandPath(_classpathFile, context); + verifyClassPath(fullPath); + // New config instance created just as needed for testing - _config = buildConfig(expandPath(_classpathFile, context)); + _config = buildConfig(fullPath); // Remove current locator and replace w/ one that always returns a custom config getPerTestMethodStore(context).put( @@ -137,7 +146,7 @@ protected void removeEnvLoaders(AndHowConfiguration buildConfig(String classpathFile) { AndHowConfiguration config = new StdConfig.StdConfigImpl(); removeEnvLoaders(config); - config.setClasspathPropFilePath(classpathFile); + config.setClasspathPropFilePath(classpathFile).classpathPropertiesRequired(); return config; } @@ -169,5 +178,16 @@ protected String expandPath(String classpath, ExtensionContext context) { return fullPath; } + + /** + * Throws an exception if the passed classpath does not exist + * @param classpath + */ + protected void verifyClassPath(String classpath) { + if (getClass().getResource(classpath) == null) { + throw new IllegalArgumentException( + "The file '" + classpath + "' could not be found on the classpath."); + } + } } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java index df829033..233c97ad 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java @@ -118,7 +118,7 @@ void completeWorkflowWithNonNullCoreAndNullConfig() throws Exception { /// /// /// /// - String cp = "my_file.properties"; + String cp = "MyPropFile.properties"; ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); @@ -218,7 +218,7 @@ void completeWorkflowWithNullCoreAndNonNullConfig() throws Exception { /// /// /// /// - String cp = "my_file.properties"; + String cp = "MyPropFile.properties"; ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java index 7cae058c..6e333717 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java @@ -1,14 +1,190 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.mockito.*; +import org.junit.jupiter.api.extension.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; +import org.junit.jupiter.api.parallel.Execution; import org.yarnandtail.andhow.*; +import org.yarnandtail.andhow.property.StrProp; import org.yarnandtail.andhow.testutil.AndHowTestUtils; +import java.lang.reflect.Method; + import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.times; +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) class ConfigFromFileExtUsageTest { + protected static ExtensionContext extensionContextDuringTest; + + @RegisterExtension + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile.properties"); + + @Order(1) + @Test + @ExtendWith(TestInterceptor.class) + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Nested + @Order(2) + class Nest1 { + + @RegisterExtension + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile2.properties"); + + @Test + @ExtendWith(TestInterceptor.class) + public void test2() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test2", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); + + assertEquals("Bob2", Config.MY_PROP.getValue()); + } + } + + @Nested + @Order(3) + class Nest2 { + + @RegisterExtension + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile3.properties"); + + @Test + @ExtendWith(TestInterceptor.class) + public void test3() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test3", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); + + assertEquals("Bob3", Config.MY_PROP.getValue()); + } + } + + + static class NonNested3 { + @RegisterExtension + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile.properties"); + + + @Order(4) + @Test + @ExtendWith(TestInterceptor.class) + public void test4() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test4", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + } + + @Order(5) + @Test + @ExtendWith(TestInterceptor.class) + public void test5() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test5", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + + /** + * This Interceptor should capture the ExtensionContext being used during the test and store it + * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated + * w/ @ExtendWith(TestInterceptor.class), the extensionContextDuringTest will contain the extension + * context of the test. + * + * Note that '@ExtendWith(TestInterceptor.class)' should be the first ExtendWith annotation to + * ensure it executes first. + */ + static class TestInterceptor implements InvocationInterceptor { + + @Override + public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable { + + Throwable throwable = null; + + try { + extensionContextDuringTest = extensionContext; + invocation.proceed(); + extensionContextDuringTest = null; + } catch (Throwable t) { + throwable = t; + } + + if (throwable != null) { + throw throwable; + } + } + + } + + /** Simple config for use in this test */ + static interface Config { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); + } } \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile.properties new file mode 100644 index 00000000..b7813761 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile.properties @@ -0,0 +1 @@ +MY_PROP: Bob \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties new file mode 100644 index 00000000..3091981b --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties @@ -0,0 +1 @@ +MY_PROP: Bob2 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties new file mode 100644 index 00000000..d8787d6f --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties @@ -0,0 +1 @@ +MY_PROP: Bob3 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties new file mode 100644 index 00000000..6ea936ef --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties @@ -0,0 +1 @@ +MY_PROP: Bob4 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 93f01e36..22b72cc7 100644 --- a/pom.xml +++ b/pom.xml @@ -106,22 +106,11 @@ - org.junit.platform - junit-platform-launcher - 1.7.2 - test - - - org.junit.jupiter - junit-jupiter-api - 5.7.2 - test - - - org.junit.jupiter - junit-jupiter-engine - 5.7.2 - test + org.junit + junit-bom + 5.9.1 + pom + import org.hamcrest @@ -140,12 +129,6 @@ 4.13.1 test - - org.junit.vintage - junit-vintage-engine - 5.7.2 - test - org.springframework @@ -205,14 +188,17 @@ org.junit.platform junit-platform-launcher + test org.junit.jupiter junit-jupiter-engine + test org.junit.jupiter junit-jupiter-api + test org.hamcrest From 797e4f2380e8d2f92f71355459fada1d2e25d09f Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Fri, 28 Oct 2022 21:03:35 -0500 Subject: [PATCH 06/22] Fix compile errors due to using features of newer JDKs than 1.8 --- .../andhow/testutil/AndHowTestUtils.java | 4 +- .../andhow/junit5/ext/ConfigFromFileExt.java | 9 ++- .../ext/ConfigFromFileExtUsageTest.java | 79 ++++++------------- .../andhow/junit5/ext/MyPropFile2.properties | 1 - .../andhow/junit5/ext/MyPropFile3.properties | 1 - .../andhow/junit5/ext/MyPropFile4.properties | 1 - .../junit5/ext/MyPropFileNest1.properties | 1 + .../junit5/ext/MyPropFileNest2.properties | 1 + 8 files changed, 37 insertions(+), 60 deletions(-) delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest1.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest2.properties diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index 66316235..bf974a0c 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -1,6 +1,6 @@ package org.yarnandtail.andhow.testutil; -import java.util.List; +import java.util.*; import java.util.function.UnaryOperator; /** @@ -212,7 +212,7 @@ public static List> setConfigurationOverrideGroups( public static List> setConfigurationOverrideGroups( Object configurationInstance, Class clazz) { - return setConfigurationOverrideGroups(configurationInstance, List.of(clazz)); + return setConfigurationOverrideGroups(configurationInstance, Arrays.asList(clazz)); } /** diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index 9f264835..1a6485d1 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -169,7 +169,14 @@ protected String expandPath(String classpath, ExtensionContext context) { String fullPath = classpath.trim(); if (! fullPath.startsWith("/")) { - String pkgName = context.getRequiredTestClass().getPackageName(); + + String pkgName = ""; //empty is correct for default pkg + + if (context.getRequiredTestClass().getPackage() != null) { + //getPackage() returns null for the default pkg + pkgName = context.getRequiredTestClass().getPackage().getName(); + } + String pkgPath = pkgName.replace(".", "/"); if (pkgPath.length() > 0) pkgPath = "/" + pkgPath; diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java index 6e333717..f212c1b3 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java @@ -48,110 +48,81 @@ public void test1() throws NoSuchMethodException { assertEquals("Bob", Config.MY_PROP.getValue()); } - @Nested @Order(2) - class Nest1 { - - @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile2.properties"); + @Test + @ExtendWith(TestInterceptor.class) + public void test2() throws NoSuchMethodException { - @Test - @ExtendWith(TestInterceptor.class) - public void test2() throws NoSuchMethodException { + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test2", null)); - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test2", null)); + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - assertEquals("Bob2", Config.MY_PROP.getValue()); - } + assertEquals("Bob", Config.MY_PROP.getValue()); } @Nested - @Order(3) - class Nest2 { + @Order(1) + class Nest1 { @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile3.properties"); + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFileNest1.properties"); @Test @ExtendWith(TestInterceptor.class) - public void test3() throws NoSuchMethodException { + public void test1() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); assertNotNull(extensionContextDuringTest); ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test3", null)); + getClass().getMethod("test1", null)); ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); assertNotNull(store); AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - assertEquals("Bob3", Config.MY_PROP.getValue()); + assertEquals("BobNest1", Config.MY_PROP.getValue()); } } + @Nested + @Order(2) + class Nest2 { - static class NonNested3 { @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile.properties"); + ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFileNest2.properties"); - - @Order(4) @Test @ExtendWith(TestInterceptor.class) - public void test4() throws NoSuchMethodException { + public void test1() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); assertNotNull(extensionContextDuringTest); ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test4", null)); + getClass().getMethod("test1", null)); ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); assertNotNull(store); AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - assertEquals("Bob", Config.MY_PROP.getValue()); + assertEquals("BobNest2", Config.MY_PROP.getValue()); } } - @Order(5) - @Test - @ExtendWith(TestInterceptor.class) - public void test5() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test5", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - - assertEquals("Bob", Config.MY_PROP.getValue()); - } - - /** * This Interceptor should capture the ExtensionContext being used during the test and store it * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties deleted file mode 100644 index 3091981b..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties +++ /dev/null @@ -1 +0,0 @@ -MY_PROP: Bob2 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties deleted file mode 100644 index d8787d6f..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile3.properties +++ /dev/null @@ -1 +0,0 @@ -MY_PROP: Bob3 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties deleted file mode 100644 index 6ea936ef..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile4.properties +++ /dev/null @@ -1 +0,0 @@ -MY_PROP: Bob4 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest1.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest1.properties new file mode 100644 index 00000000..33f9012a --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest1.properties @@ -0,0 +1 @@ +MY_PROP: BobNest1 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest2.properties new file mode 100644 index 00000000..a994333c --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFileNest2.properties @@ -0,0 +1 @@ +MY_PROP: BobNest2 \ No newline at end of file From 878e3d98233e1d1942b63080114e56c45019df33 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Sat, 29 Oct 2022 00:19:25 -0500 Subject: [PATCH 07/22] Added a custom annotation for config from a file --- .../andhow/junit5/ConfigFromFile.java | 17 ++ .../andhow/junit5/ext/ConfigFromFileExt.java | 47 +++++- .../junit5/ConfigFromFileUsageTest.java | 156 ++++++++++++++++++ pom.xml | 2 + 4 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java new file mode 100644 index 00000000..39c87372 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java @@ -0,0 +1,17 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ TYPE, METHOD, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@Inherited +@ExtendWith(ConfigFromFileExt.class) +public @interface ConfigFromFile { + String filePath(); +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java index 1a6485d1..d019139a 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java @@ -3,10 +3,13 @@ import org.junit.jupiter.api.extension.*; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.StandardLoader; +import org.yarnandtail.andhow.junit5.ConfigFromFile; import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.testutil.AndHowTestUtils; +import java.lang.reflect.Method; import java.util.List; +import java.util.Optional; import java.util.function.UnaryOperator; public class ConfigFromFileExt extends ExtensionBase @@ -29,6 +32,46 @@ public ConfigFromFileExt(String classpathFile) { _classpathFile = classpathFile; } + public ConfigFromFileExt() { + // Empty constructor used when the @ConfigFromFile annotation is used. + // In that case, the classpathFile is found via the annotation filePath property. + } + + // Would this be confused, called for BeforeAll and BeforeEach, etc, if there + // were both class and test annotations on a test? How would that be kept straight? + // Might need to split up these + protected String getClasspathFile(ExtensionContext context) { + if (_classpathFile == null) { + Class clazz = context.getRequiredTestClass(); + Optional method = context.getTestMethod(); + + ConfigFromFile cff; + + if (method.isPresent()) { + cff = method.get().getAnnotation(ConfigFromFile.class); + + if (cff == null) { + throw new IllegalStateException("Expected the @ConfigFromFile annotation on the '" + + method.get().getName() + "' method."); + } + + } else { + + cff = clazz.getAnnotation(ConfigFromFile.class); + + if (cff == null) { + throw new IllegalStateException("Expected the @ConfigFromFile annotation on class '" + + clazz.getName() + "'."); + } + + } + + _classpathFile = cff.filePath(); + } + + return _classpathFile; + } + /** Key to store the AndHowCore (if any) of AndHow. */ protected static final String CORE_KEY = "core_key"; @@ -59,7 +102,7 @@ public void beforeAll(ExtensionContext context) throws Exception { // remove current core and keep to be later restored getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); - String fullPath = expandPath(_classpathFile, context); + String fullPath = expandPath(getClasspathFile(context), context); verifyClassPath(fullPath); // New config instance created just as needed for testing @@ -92,7 +135,7 @@ public void afterAll(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); - String fullPath = expandPath(_classpathFile, context); + String fullPath = expandPath(getClasspathFile(context), context); verifyClassPath(fullPath); // New config instance created just as needed for testing diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java new file mode 100644 index 00000000..45a9bf39 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java @@ -0,0 +1,156 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.api.parallel.Execution; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; +import org.yarnandtail.andhow.property.StrProp; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) +class ConfigFromFileUsageTest { + + protected static ExtensionContext extensionContextDuringTest; + + @Order(1) + @Test + @ExtendWith(TestInterceptor.class) + @ConfigFromFile(filePath = "ext/MyPropFile.properties") + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Order(2) + @Test + @ExtendWith(TestInterceptor.class) + @ConfigFromFile(filePath = "ext/MyPropFile.properties") + public void test2() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test2", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Nested + @Order(1) + @ConfigFromFile(filePath = "ext/MyPropFileNest1.properties") + class Nest1 { + + @Test + @ExtendWith(TestInterceptor.class) + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); + + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + } + + @Nested + @Order(2) + @ConfigFromFile(filePath = "ext/MyPropFileNest2.properties") + class Nest2 { + + @Test + @ExtendWith(TestInterceptor.class) + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); + + assertEquals("BobNest2", Config.MY_PROP.getValue()); + } + } + + /** + * This Interceptor should capture the ExtensionContext being used during the test and store it + * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated + * w/ @ExtendWith(TestInterceptor.class), the extensionContextDuringTest will contain the extension + * context of the test. + * + * Note that '@ExtendWith(TestInterceptor.class)' should be the first ExtendWith annotation to + * ensure it executes first. + */ + static class TestInterceptor implements InvocationInterceptor { + + @Override + public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable { + + Throwable throwable = null; + + try { + extensionContextDuringTest = extensionContext; + invocation.proceed(); + extensionContextDuringTest = null; + } catch (Throwable t) { + throwable = t; + } + + if (throwable != null) { + throw throwable; + } + } + + } + + /** Simple config for use in this test */ + static interface Config { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 22b72cc7..d75082f9 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,8 @@ UTF-8 + 1.8 + 1.8 none true true From 0f8797f9ae34622dd4e7be429a67012bbda2dddb Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Mon, 31 Oct 2022 23:31:58 -0500 Subject: [PATCH 08/22] Large refactor --- .../junit5/ConfigFromFileBeforeAllTests.java | 16 ++ ...java => ConfigFromFileBeforeEachTest.java} | 8 +- .../junit5/ConfigFromFileBeforeThisTest.java | 19 +++ ...ileExt.java => ConfigFromFileBaseExt.java} | 73 ++++---- .../ext/ConfigFromFileBeforeAllTestsExt.java | 47 +++++ .../ext/ConfigFromFileBeforeEachTestExt.java | 47 +++++ .../ext/ConfigFromFileBeforeThisTestExt.java | 47 +++++ ...figFromFileBaseExtDefaultPackageTest.java} | 16 +- .../junit5/ConfigFromFileMixedUsageTest.java | 140 +++++++++++++++ .../junit5/ConfigFromFileUsageTest.java | 156 ----------------- ...figFromFileBeforeAllTestsExtUsageTest.java | 100 +++++++++++ ...figFromFileBeforeEachTestExtUsageTest.java | 99 +++++++++++ .../junit5/ext/ConfigFromFileExtUnitTest.java | 9 +- .../ext/ConfigFromFileExtUsageTest.java | 161 ------------------ .../junit5/ext/InterceptorTestBase.java | 47 +++++ .../andhow/junit5/ext/MyPropFile2.properties | 1 + 16 files changed, 613 insertions(+), 373 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/{ConfigFromFile.java => ConfigFromFileBeforeEachTest.java} (57%) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/{ConfigFromFileExt.java => ConfigFromFileBaseExt.java} (83%) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/{ConfigFromFileExtDefaultPackageTest.java => ConfigFromFileBaseExtDefaultPackageTest.java} (85%) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExtUsageTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExtUsageTest.java delete mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java new file mode 100644 index 00000000..1f6a35b4 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java @@ -0,0 +1,16 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeAllTestsExt; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ TYPE, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@ExtendWith(ConfigFromFileBeforeAllTestsExt.class) +public @interface ConfigFromFileBeforeAllTests { + String filePath(); +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java similarity index 57% rename from junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java rename to junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java index 39c87372..4cd990ec 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFile.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java @@ -1,17 +1,17 @@ package org.yarnandtail.andhow.junit5; import org.junit.jupiter.api.extension.ExtendWith; -import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeEachTestExt; import java.lang.annotation.*; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Target({ TYPE, METHOD, ANNOTATION_TYPE }) +@Target({ TYPE, ANNOTATION_TYPE }) @Retention(RUNTIME) @Inherited -@ExtendWith(ConfigFromFileExt.class) -public @interface ConfigFromFile { +@ExtendWith(ConfigFromFileBeforeEachTestExt.class) +public @interface ConfigFromFileBeforeEachTest { String filePath(); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java new file mode 100644 index 00000000..6123391c --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java @@ -0,0 +1,19 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBaseExt; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeThisTestExt; + +import java.lang.annotation.*; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({ METHOD, ANNOTATION_TYPE }) +@Retention(RUNTIME) +@Inherited +@ExtendWith(ConfigFromFileBeforeThisTestExt.class) +public @interface ConfigFromFileBeforeThisTest { + String filePath(); +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java similarity index 83% rename from junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java rename to junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java index d019139a..1a4d9cd1 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java @@ -3,17 +3,21 @@ import org.junit.jupiter.api.extension.*; import org.yarnandtail.andhow.*; import org.yarnandtail.andhow.api.StandardLoader; -import org.yarnandtail.andhow.junit5.ConfigFromFile; import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.testutil.AndHowTestUtils; -import java.lang.reflect.Method; import java.util.List; -import java.util.Optional; -import java.util.function.UnaryOperator; -public class ConfigFromFileExt extends ExtensionBase - implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { +/** + * Implementation of an ExtensionBase that configures AndHow from a single properties + * file. It is the base class to be used in one of three modes: + *
  • BeforeAll/AfterAll Registered on a test class
  • + *
  • BeforeEach/AfterEach Registered on a test class to apply to all methods
  • + *
  • BeforeEach/AfterEach Registered on a test method to apply to a single test
  • + * Mixing purposes (i.e. a single instance registered for use for more than one mode) + * can result in confused state. + */ +public abstract class ConfigFromFileBaseExt extends ExtensionBase { /** The complete path to a properties file on the classpath */ private String _classpathFile; @@ -25,50 +29,34 @@ public class ConfigFromFileExt extends ExtensionBase * New instance - validation of the classpathFile is deferred until use. * @param classpathFile Complete path to a properties file on the classpath */ - public ConfigFromFileExt(String classpathFile) { + public ConfigFromFileBaseExt(String classpathFile) { if (classpathFile == null) { throw new IllegalArgumentException("The classpath properties file path cannot be null."); } _classpathFile = classpathFile; } - public ConfigFromFileExt() { - // Empty constructor used when the @ConfigFromFile annotation is used. - // In that case, the classpathFile is found via the annotation filePath property. - } + /** + * Empty constructor used when the \@ConfigFromFile annotation is used. + * + * When the empty construct is used, the classpathFile is found via the + * \@ConfigFromFile annotation filePath property. + */ + public ConfigFromFileBaseExt() { } + + /** + * When this Extension is being used in association w/ an annotation, this returns + * that class. + * @return + */ + protected abstract String getAnnotationFilePath(ExtensionContext context); - // Would this be confused, called for BeforeAll and BeforeEach, etc, if there - // were both class and test annotations on a test? How would that be kept straight? - // Might need to split up these protected String getClasspathFile(ExtensionContext context) { if (_classpathFile == null) { - Class clazz = context.getRequiredTestClass(); - Optional method = context.getTestMethod(); - - ConfigFromFile cff; - - if (method.isPresent()) { - cff = method.get().getAnnotation(ConfigFromFile.class); - - if (cff == null) { - throw new IllegalStateException("Expected the @ConfigFromFile annotation on the '" + - method.get().getName() + "' method."); - } - - } else { - - cff = clazz.getAnnotation(ConfigFromFile.class); - - if (cff == null) { - throw new IllegalStateException("Expected the @ConfigFromFile annotation on class '" + - clazz.getName() + "'."); - } - - } - - _classpathFile = cff.filePath(); + _classpathFile = getAnnotationFilePath(context); } + System.out.println("CFF: filePath = " + _classpathFile); return _classpathFile; } @@ -96,7 +84,6 @@ protected String getClasspathFile(ExtensionContext context) { * @param context Passed by JUnit prior to any test in the class * @throws Exception */ - @Override public void beforeAll(ExtensionContext context) throws Exception { // remove current core and keep to be later restored @@ -113,9 +100,9 @@ public void beforeAll(ExtensionContext context) throws Exception { CONFIG_KEY, AndHowTestUtils.setAndHowInProcessConfig(_config)); + System.out.println("CFF: beforeAll: filePath = " + fullPath); } - @Override public void afterAll(ExtensionContext context) throws Exception { Object core = getPerTestClassStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); AndHowTestUtils.setAndHowCore(core); @@ -131,7 +118,6 @@ public void afterAll(ExtensionContext context) throws Exception { * @param context * @throws Exception */ - @Override public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); @@ -145,6 +131,8 @@ public void beforeEach(ExtensionContext context) throws Exception { getPerTestMethodStore(context).put( CONFIG_KEY, AndHowTestUtils.setAndHowInProcessConfig(_config)); + + System.out.println("CFF: beforeEach: filePath = " + fullPath); } /** @@ -152,7 +140,6 @@ public void beforeEach(ExtensionContext context) throws Exception { * @param context * @throws Exception */ - @Override public void afterEach(ExtensionContext context) throws Exception { Object core = getPerTestMethodStore(context).remove(CORE_KEY, AndHowTestUtils.getAndHowCoreClass()); AndHowTestUtils.setAndHowCore(core); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java new file mode 100644 index 00000000..b5618f03 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java @@ -0,0 +1,47 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeAllTests; + +public class ConfigFromFileBeforeAllTestsExt extends ConfigFromFileBaseExt + implements BeforeAllCallback, AfterAllCallback { + + /** + * Empty constructor used when the \@ConfigFromFileBeforeThisTest annotation is used. + * + * When the empty construct is used, the classpathFile is found via the + * \@ConfigFromFile annotation filePath property. + */ + public ConfigFromFileBeforeAllTestsExt() { + super(); + } + + /** + * New instance - validation of the classpathFile is deferred until use. + * @param classpathFile Complete path to a properties file on the classpath + */ + public ConfigFromFileBeforeAllTestsExt(String classpathFile) { + super(classpathFile); + } + + @Override + protected String getAnnotationFilePath(ExtensionContext context) { + if (context.getElement().isPresent()) { + ConfigFromFileBeforeAllTests cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeAllTests.class); + return cff.filePath(); + } else { + throw new IllegalStateException("Expected the @ConfigFromFileBeforeAllTests annotation on the '" + + context.getRequiredTestMethod() + "' test method."); + } + } + + @Override + public void beforeAll(final ExtensionContext context) throws Exception { + super.beforeAll(context); + } + + @Override + public void afterAll(final ExtensionContext context) throws Exception { + super.afterAll(context); + } +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java new file mode 100644 index 00000000..264273b2 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java @@ -0,0 +1,47 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeEachTest; + +public class ConfigFromFileBeforeEachTestExt extends ConfigFromFileBaseExt + implements BeforeEachCallback, AfterEachCallback { + + /** + * Empty constructor used when the \@ConfigFromFileBeforeThisTest annotation is used. + * + * When the empty construct is used, the classpathFile is found via the + * \@ConfigFromFile annotation filePath property. + */ + public ConfigFromFileBeforeEachTestExt() { + super(); + } + + /** + * New instance - validation of the classpathFile is deferred until use. + * @param classpathFile Complete path to a properties file on the classpath + */ + public ConfigFromFileBeforeEachTestExt(String classpathFile) { + super(classpathFile); + } + + @Override + protected String getAnnotationFilePath(ExtensionContext context) { + if (context.getElement().isPresent()) { + ConfigFromFileBeforeEachTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeEachTest.class); + return cff.filePath(); + } else { + throw new IllegalStateException("Expected the @ConfigFromFileBeforeEachTest annotation on the '" + + context.getRequiredTestMethod() + "' test method."); + } + } + + @Override + public void beforeEach(final ExtensionContext context) throws Exception { + super.beforeEach(context); + } + + @Override + public void afterEach(final ExtensionContext context) throws Exception { + super.afterEach(context); + } +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java new file mode 100644 index 00000000..c61c5bd0 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java @@ -0,0 +1,47 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeThisTest; + +public class ConfigFromFileBeforeThisTestExt extends ConfigFromFileBaseExt + implements BeforeEachCallback, AfterEachCallback { + + /** + * Empty constructor used when the \@ConfigFromFileBeforeThisTest annotation is used. + * + * When the empty construct is used, the classpathFile is found via the + * \@ConfigFromFile annotation filePath property. + */ + public ConfigFromFileBeforeThisTestExt() { + super(); + } + + /** + * New instance - validation of the classpathFile is deferred until use. + * @param classpathFile Complete path to a properties file on the classpath + */ + public ConfigFromFileBeforeThisTestExt(String classpathFile) { + super(classpathFile); + } + + @Override + protected String getAnnotationFilePath(ExtensionContext context) { + if (context.getElement().isPresent()) { + ConfigFromFileBeforeThisTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeThisTest.class); + return cff.filePath(); + } else { + throw new IllegalStateException("Expected the @ConfigFromFileBeforeThisTest annotation on the '" + + context.getRequiredTestMethod() + "' test method."); + } + } + + @Override + public void beforeEach(final ExtensionContext context) throws Exception { + super.beforeEach(context); + } + + @Override + public void afterEach(final ExtensionContext context) throws Exception { + super.afterEach(context); + } +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java similarity index 85% rename from junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java rename to junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java index 96685a37..b70e4810 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileExtDefaultPackageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java @@ -1,7 +1,7 @@ import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtensionContext; import org.mockito.*; -import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBaseExt; import org.yarnandtail.andhow.testutil.AndHowTestUtils; import static org.junit.jupiter.api.Assertions.*; @@ -13,7 +13,7 @@ * so the only way to refer to such is a class is to also be in the default * package, thus this test class. */ -class ConfigFromFileExtDefaultPackageTest { +class ConfigFromFileBaseExtDefaultPackageTest { Object andHowCoreCreatedDuringTest; @@ -58,7 +58,7 @@ void tearDown() { @Test public void expandPathShouldExpandRelativePaths() { - ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + ConfigFromFileBaseExtSimple ext = new ConfigFromFileBaseExtSimple(); assertEquals("/myFile.props", ext.expandPath("myFile.props", extensionContext)); @@ -71,7 +71,7 @@ public void expandPathShouldExpandRelativePaths() { @Test public void expandPathShouldNotExpandAbsPaths() { - ConfigFromFileExtSimple ext = new ConfigFromFileExtSimple(); + ConfigFromFileBaseExtSimple ext = new ConfigFromFileBaseExtSimple(); assertEquals("/myFile.props", ext.expandPath("/myFile.props", extensionContext)); @@ -89,11 +89,15 @@ public void expandPathShouldNotExpandAbsPaths() { } /* Simple subclass to test protected methods */ - public static class ConfigFromFileExtSimple extends ConfigFromFileExt { - public ConfigFromFileExtSimple() { + public static class ConfigFromFileBaseExtSimple extends ConfigFromFileBaseExt { + public ConfigFromFileBaseExtSimple() { super(""); } + public String getAnnotationFilePath(ExtensionContext context) { + return ""; + } + public String expandPath(String classpath, ExtensionContext context) { return super.expandPath(classpath, context); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java new file mode 100644 index 00000000..790d463c --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java @@ -0,0 +1,140 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.api.parallel.Execution; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.junit5.ext.*; +import org.yarnandtail.andhow.property.StrProp; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) +@ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFile.properties") +class ConfigFromFileMixedUsageTest extends InterceptorTestBase { + + private static Object coreFoundInTest1; + + @Order(1) + @Test + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + + assertEquals("Bob", Config.MY_PROP.getValue()); + + coreFoundInTest1 = AndHowTestUtils.getAndHowCore(); //Only non-null after value access above. + } + + @Order(2) + @Test + public void test2() { + assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Order(3) + @Test + @ConfigFromFileBeforeThisTest(filePath = "ext/MyPropFile2.properties") + public void test3() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeThisTestExt.class, (Class)(this.getClass()), + getClass().getMethod("test3", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + assertSame(coreFoundInTest1, store.get("core_key"), + "The stored core should be the same one created via BeforeAll in test1 "); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + + assertEquals("Bob2", Config.MY_PROP.getValue()); + } + + @Order(4) + @Test + public void test4() throws NoSuchMethodException { + assertTrue(AndHow.isInitialized()); + assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Nested + @Order(1) + @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFileNest1.properties") + class Nest1 { + + @Test + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeAllTestsExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + assertSame(coreFoundInTest1, store.get("core_key"), + "The stored core should be the same one created via BeforeAll in test1 "); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + + @Order(2) + @Test + public void test2() { + assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + + + @Order(3) + @Test + @ConfigFromFileBeforeThisTest(filePath = "ext/MyPropFile2.properties") + public void test3() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeThisTestExt.class, (Class)(this.getClass()), + getClass().getMethod("test2", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + assertNotSame(coreFoundInTest1, store.get("core_key")); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + + assertEquals("Bob2", Config.MY_PROP.getValue()); + } + + @Order(4) + @Test + public void test4() { + assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + } + + /** Simple config for use in this test */ + static interface Config { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java deleted file mode 100644 index 45a9bf39..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileUsageTest.java +++ /dev/null @@ -1,156 +0,0 @@ -package org.yarnandtail.andhow.junit5; - -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.*; -import org.junit.jupiter.api.parallel.Execution; -import org.yarnandtail.andhow.AndHow; -import org.yarnandtail.andhow.junit5.ext.ConfigFromFileExt; -import org.yarnandtail.andhow.property.StrProp; -import org.yarnandtail.andhow.testutil.AndHowTestUtils; - -import java.lang.reflect.Method; - -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; - -/** - * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext - * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. - */ -@TestClassOrder(ClassOrderer.OrderAnnotation.class) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@Execution(SAME_THREAD) -class ConfigFromFileUsageTest { - - protected static ExtensionContext extensionContextDuringTest; - - @Order(1) - @Test - @ExtendWith(TestInterceptor.class) - @ConfigFromFile(filePath = "ext/MyPropFile.properties") - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); - - - assertEquals("Bob", Config.MY_PROP.getValue()); - } - - @Order(2) - @Test - @ExtendWith(TestInterceptor.class) - @ConfigFromFile(filePath = "ext/MyPropFile.properties") - public void test2() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test2", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); - - - assertEquals("Bob", Config.MY_PROP.getValue()); - } - - @Nested - @Order(1) - @ConfigFromFile(filePath = "ext/MyPropFileNest1.properties") - class Nest1 { - - @Test - @ExtendWith(TestInterceptor.class) - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); - - assertEquals("BobNest1", Config.MY_PROP.getValue()); - } - } - - @Nested - @Order(2) - @ConfigFromFile(filePath = "ext/MyPropFileNest2.properties") - class Nest2 { - - @Test - @ExtendWith(TestInterceptor.class) - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileUsageTest.Config.class); - - assertEquals("BobNest2", Config.MY_PROP.getValue()); - } - } - - /** - * This Interceptor should capture the ExtensionContext being used during the test and store it - * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated - * w/ @ExtendWith(TestInterceptor.class), the extensionContextDuringTest will contain the extension - * context of the test. - * - * Note that '@ExtendWith(TestInterceptor.class)' should be the first ExtendWith annotation to - * ensure it executes first. - */ - static class TestInterceptor implements InvocationInterceptor { - - @Override - public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable { - - Throwable throwable = null; - - try { - extensionContextDuringTest = extensionContext; - invocation.proceed(); - extensionContextDuringTest = null; - } catch (Throwable t) { - throwable = t; - } - - if (throwable != null) { - throw throwable; - } - } - - } - - /** Simple config for use in this test */ - static interface Config { - StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); - } -} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExtUsageTest.java new file mode 100644 index 00000000..690058e8 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExtUsageTest.java @@ -0,0 +1,100 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.api.parallel.Execution; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.property.StrProp; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) +class ConfigFromFileBeforeAllTestsExtUsageTest extends InterceptorTestBase { + + @RegisterExtension + static ConfigFromFileBeforeAllTestsExt _configFromFileBaseExt = new ConfigFromFileBeforeAllTestsExt("MyPropFile.properties"); + + /** test1 will set this val. All following tests should share if working as expected. */ + private static Object coreFoundInTest1; + + @Order(1) + @Test + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeAllTestsExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileBeforeAllTestsExtUsageTest.Config.class); + + + assertEquals("Bob", Config.MY_PROP.getValue()); + + coreFoundInTest1 = AndHowTestUtils.getAndHowCore(); //Only non-null after value access above. + } + + @Order(2) + @Test + public void test2() throws NoSuchMethodException { + assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Order(1) + class Nest1 { + + // The nested class inherits the registered ConfigFromFileBeforeAllTestsExt + // extension and it is invoked again, newly creating configuration based on + // "MyPropFile.properties". + @Test + @Order(1) + @ExtendWith(TestInterceptor.class) + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeAllTestsExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileBeforeAllTestsExtUsageTest.Config.class); + + assertEquals("Bob", Config.MY_PROP.getValue()); + + coreFoundInTest1 = AndHowTestUtils.getAndHowCore(); //Only non-null after value access above. + + } + + @Order(2) + @Test + public void test2() throws NoSuchMethodException { + assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); + } + } + + /** Simple config for use in this test */ + static interface Config { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExtUsageTest.java new file mode 100644 index 00000000..b593afd9 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExtUsageTest.java @@ -0,0 +1,99 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.api.parallel.Execution; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.property.StrProp; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) +class ConfigFromFileBeforeEachTestExtUsageTest extends InterceptorTestBase { + + @RegisterExtension + static ConfigFromFileBeforeEachTestExt _configFromFileBaseExt = new ConfigFromFileBeforeEachTestExt("MyPropFile.properties"); + + @Order(1) + @Test + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeEachTestExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileBeforeEachTestExtUsageTest.Config.class); + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + // test2 is the same as test1 - They should each have a newly configured state + // based on "MyPropFile.properties" + @Order(2) + @Test + public void test2() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeEachTestExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileBeforeEachTestExtUsageTest.Config.class); + + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + @Order(1) + class Nest1 { + + // The nested class inherits the registered ConfigFromFileBeforeEachTestExt + // extension and it is invoked again, newly creating configuration based on + // "MyPropFile.properties". + @Test + @Order(1) + @ExtendWith(TestInterceptor.class) + public void test1() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + assertNotNull(extensionContextDuringTest); + + ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( + ConfigFromFileBeforeEachTestExt.class, (Class)(this.getClass()), + getClass().getMethod("test1", null)); + + ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); + assertNotNull(store); + + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileBeforeEachTestExtUsageTest.Config.class); + + assertEquals("Bob", Config.MY_PROP.getValue()); + + } + } + + /** Simple config for use in this test */ + static interface Config { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java index 233c97ad..a600daec 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java @@ -285,22 +285,25 @@ void completeWorkflowWithNullCoreAndNonNullConfig() throws Exception { } /* Simple subclass to test protected methods */ - public static class ConfigFromFileExtSimple extends ConfigFromFileExt { + public static class ConfigFromFileExtSimple extends ConfigFromFileBaseExt { public ConfigFromFileExtSimple() { super(""); } + @Override + protected String getAnnotationFilePath(final ExtensionContext context) { return null; } + public ConfigFromFileExtSimple(String classpathFile) { super(classpathFile); } public static String getCoreKey() { - return ConfigFromFileExt.CORE_KEY; + return ConfigFromFileBaseExt.CORE_KEY; } public static String getConfigKey() { - return ConfigFromFileExt.CONFIG_KEY; + return ConfigFromFileBaseExt.CONFIG_KEY; } public String expandPath(String classpath, ExtensionContext context) { diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java deleted file mode 100644 index f212c1b3..00000000 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUsageTest.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.yarnandtail.andhow.junit5.ext; - -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.*; -import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; -import org.junit.jupiter.api.parallel.Execution; -import org.yarnandtail.andhow.*; -import org.yarnandtail.andhow.property.StrProp; -import org.yarnandtail.andhow.testutil.AndHowTestUtils; - -import java.lang.reflect.Method; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.times; - -/** - * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext - * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. - */ -@TestClassOrder(ClassOrderer.OrderAnnotation.class) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@Execution(SAME_THREAD) -class ConfigFromFileExtUsageTest { - - protected static ExtensionContext extensionContextDuringTest; - - @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFile.properties"); - - @Order(1) - @Test - @ExtendWith(TestInterceptor.class) - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - - assertEquals("Bob", Config.MY_PROP.getValue()); - } - - @Order(2) - @Test - @ExtendWith(TestInterceptor.class) - public void test2() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test2", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - - assertEquals("Bob", Config.MY_PROP.getValue()); - } - - @Nested - @Order(1) - class Nest1 { - - @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFileNest1.properties"); - - @Test - @ExtendWith(TestInterceptor.class) - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - assertEquals("BobNest1", Config.MY_PROP.getValue()); - } - } - - @Nested - @Order(2) - class Nest2 { - - @RegisterExtension - ConfigFromFileExt configFromFileExt = new ConfigFromFileExt("MyPropFileNest2.properties"); - - @Test - @ExtendWith(TestInterceptor.class) - public void test1() throws NoSuchMethodException { - - assertFalse(AndHow.isInitialized()); - assertNotNull(extensionContextDuringTest); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileExtUsageTest.Config.class); - - assertEquals("BobNest2", Config.MY_PROP.getValue()); - } - } - - /** - * This Interceptor should capture the ExtensionContext being used during the test and store it - * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated - * w/ @ExtendWith(TestInterceptor.class), the extensionContextDuringTest will contain the extension - * context of the test. - * - * Note that '@ExtendWith(TestInterceptor.class)' should be the first ExtendWith annotation to - * ensure it executes first. - */ - static class TestInterceptor implements InvocationInterceptor { - - @Override - public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable { - - Throwable throwable = null; - - try { - extensionContextDuringTest = extensionContext; - invocation.proceed(); - extensionContextDuringTest = null; - } catch (Throwable t) { - throwable = t; - } - - if (throwable != null) { - throw throwable; - } - } - - } - - /** Simple config for use in this test */ - static interface Config { - StrProp MY_PROP = StrProp.builder().aliasInAndOut("MY_PROP").build(); - } -} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java new file mode 100644 index 00000000..3a988d15 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java @@ -0,0 +1,47 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.extension.*; +import java.lang.reflect.Method; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@ExtendWith(ConfigFromFileBeforeAllTestsExtUsageTest.TestInterceptor.class) +public +class InterceptorTestBase { + + protected static ExtensionContext extensionContextDuringTest; + + /** + * This Interceptor should capture the ExtensionContext being used during the test and store it + * to the parent class's extensionContextDuringTest static var. Within a @Test method annotated + * w/ @ExtendWith(TestInterceptor.class), the extensionContextDuringTest will contain the extension + * context of the test. + * + * Note that '@ExtendWith(TestInterceptor.class)' should be the first ExtendWith annotation to + * ensure it executes first. + */ + public static class TestInterceptor implements InvocationInterceptor { + + @Override + public void interceptTestMethod(final Invocation invocation, final ReflectiveInvocationContext invocationContext, final ExtensionContext extensionContext) throws Throwable { + + Throwable throwable = null; + + try { + extensionContextDuringTest = extensionContext; + invocation.proceed(); + extensionContextDuringTest = null; + } catch (Throwable t) { + throwable = t; + } + + if (throwable != null) { + throw throwable; + } + } + + } + +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties new file mode 100644 index 00000000..3091981b --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/ext/MyPropFile2.properties @@ -0,0 +1 @@ +MY_PROP: Bob2 \ No newline at end of file From b39637c54a33282d0a309bff71c501990f550439 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 3 Nov 2022 15:54:43 -0500 Subject: [PATCH 09/22] Fixed several issues and all tests working --- .../andhow/testutil/AndHowTestUtils.java | 4 +- .../junit5/ext/ConfigFromFileBaseExt.java | 46 ++++-- .../ext/ConfigFromFileBeforeAllTestsExt.java | 25 ++- .../ext/ConfigFromFileBeforeEachTestExt.java | 26 +++- .../ext/ConfigFromFileBeforeThisTestExt.java | 9 ++ .../junit5/ConfigFromFileMixedUsageTest.java | 146 ++++++++++++++++-- .../junit5/ext/InterceptorTestBase.java | 3 +- 7 files changed, 220 insertions(+), 39 deletions(-) diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index bf974a0c..f7ca3bde 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -204,14 +204,14 @@ public static UnaryOperator setAndHowConfigLocator(UnaryOperator newLo } public static List> setConfigurationOverrideGroups( - Object configurationInstance, List> classList) { + Object configurationInstance, List classList) { return ReflectionTestUtils.setInstanceFieldValue( configurationInstance, "overrideGroups", classList, List.class); } public static List> setConfigurationOverrideGroups( - Object configurationInstance, Class clazz) { + Object configurationInstance, Class clazz) { return setConfigurationOverrideGroups(configurationInstance, Arrays.asList(clazz)); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java index 1a4d9cd1..5a5d4c82 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java @@ -16,6 +16,18 @@ *
  • BeforeEach/AfterEach Registered on a test method to apply to a single test
  • * Mixing purposes (i.e. a single instance registered for use for more than one mode) * can result in confused state. + * + * Note that JUnit has an unexpected handling of nested test classes when the parent class and the + * nested test class have the same annotation: JUnit creates only a single instance of an Extension. + * Thus, no state can be kept in the extension other than via the context store mechanism, or, for + * config info, read configuration from annotation parameters EACH TIME. Configuration parameters + * cannot be cached (other than on the context store) because the cached values will bleed over from + * the parent class to the child class. For instance, if the parent class is annotated with: + * @ConfigFromFileBeforeAllTests(filePath = "parent.properties") + * and the nested class is annotated with: + * @ConfigFromFileBeforeAllTests(filePath = "child.properties") + * storing the config value of the parent in the extension means that the cached value will be found + * and used when the child is configured. Read from the annotation each time! */ public abstract class ConfigFromFileBaseExt extends ExtensionBase { @@ -51,15 +63,6 @@ public ConfigFromFileBaseExt() { } */ protected abstract String getAnnotationFilePath(ExtensionContext context); - protected String getClasspathFile(ExtensionContext context) { - if (_classpathFile == null) { - _classpathFile = getAnnotationFilePath(context); - } - - System.out.println("CFF: filePath = " + _classpathFile); - return _classpathFile; - } - /** Key to store the AndHowCore (if any) of AndHow. */ protected static final String CORE_KEY = "core_key"; @@ -100,7 +103,10 @@ public void beforeAll(ExtensionContext context) throws Exception { CONFIG_KEY, AndHowTestUtils.setAndHowInProcessConfig(_config)); - System.out.println("CFF: beforeAll: filePath = " + fullPath); +// System.out.println("CFF:beforeAll = " + getClasspathFile(context) + " for " + +// context.getElement().get().toString() +// .replaceFirst("org\\.yarnandtail\\.andhow\\.junit5\\.", "") + +// " instance: " + this.toString()); } public void afterAll(ExtensionContext context) throws Exception { @@ -132,7 +138,11 @@ public void beforeEach(ExtensionContext context) throws Exception { CONFIG_KEY, AndHowTestUtils.setAndHowInProcessConfig(_config)); - System.out.println("CFF: beforeEach: filePath = " + fullPath); +// System.out.println("CFF:beforeEach = " + getClasspathFile(context) + " for " + +// context.getElement().get().toString() +// //.replaceFirst("org\\.yarnandtail\\.andhow\\.junit5\\.", "") +// .replaceFirst("public void org\\.yarnandtail\\.andhow\\.junit5\\.", "") +// .replaceFirst("\\(\\).*", "()")); } /** @@ -149,6 +159,20 @@ public void afterEach(ExtensionContext context) throws Exception { AndHowTestUtils.setAndHowInProcessConfig(config); } + /** + * Find the user configured properties file path. + *

    + * If configured via an annotation, read the annotated value EVERY TIME, DO NOT CACHE THE VALUE. + * @param context + * @return + */ + protected String getClasspathFile(ExtensionContext context) { + if (_classpathFile == null) { + return getAnnotationFilePath(context); + } else { + return _classpathFile; + } + } /** * Remove the Loaders that are 'environment' related, meaning they could be set diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java index b5618f03..6a67c390 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java @@ -24,15 +24,30 @@ public ConfigFromFileBeforeAllTestsExt(String classpathFile) { super(classpathFile); } + /** + * Find the annotated filePath property in the @ConfigFromFileBeforeAllTests annotation on the + * test class. + *

    + * In the case of a @Nested test, the ConfigFromFileBeforeAllTestsExt is invoked again for each + * nested test class so the code recursively hunts up the inheritance chain to find the class + * with the annotation. + * + * @param context + * @return + */ @Override protected String getAnnotationFilePath(ExtensionContext context) { - if (context.getElement().isPresent()) { - ConfigFromFileBeforeAllTests cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeAllTests.class); + + ConfigFromFileBeforeAllTests cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeAllTests.class); + + if (cff != null) { return cff.filePath(); - } else { - throw new IllegalStateException("Expected the @ConfigFromFileBeforeAllTests annotation on the '" + - context.getRequiredTestMethod() + "' test method."); + } else if (context.getParent().isPresent()) { + return getAnnotationFilePath(context.getParent().get()); } + + throw new IllegalStateException("Expected the @ConfigFromFileBeforeAllTests annotation on the '" + + context.getRequiredTestClass() + "' class or a parent class for a @Nested test."); } @Override diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java index 264273b2..fd8608e8 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java @@ -1,6 +1,7 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeAllTests; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeEachTest; public class ConfigFromFileBeforeEachTestExt extends ConfigFromFileBaseExt @@ -24,15 +25,30 @@ public ConfigFromFileBeforeEachTestExt(String classpathFile) { super(classpathFile); } + /** + * Find the annotated filePath property in the @ConfigFromFileBeforeEachTest annotation on the + * test class. + *

    + * In the case of a @Nested test, the ConfigFromFileBeforeEachTestExt is invoked again for each + * nested test class so the code recursively hunts up the inheritance chain to find the class + * with the annotation. + * + * @param context + * @return + */ @Override protected String getAnnotationFilePath(ExtensionContext context) { - if (context.getElement().isPresent()) { - ConfigFromFileBeforeEachTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeEachTest.class); + + ConfigFromFileBeforeEachTest cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeEachTest.class); + + if (cff != null) { return cff.filePath(); - } else { - throw new IllegalStateException("Expected the @ConfigFromFileBeforeEachTest annotation on the '" + - context.getRequiredTestMethod() + "' test method."); + } else if (context.getParent().isPresent()) { + return getAnnotationFilePath(context.getParent().get()); } + + throw new IllegalStateException("Expected the @ConfigFromFileBeforeEachTest annotation on the '" + + context.getRequiredTestClass() + "' class or a parent class for a @Nested test."); } @Override diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java index c61c5bd0..5c5a36b4 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java @@ -1,6 +1,7 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.extension.*; +import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeEachTest; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeThisTest; public class ConfigFromFileBeforeThisTestExt extends ConfigFromFileBaseExt @@ -24,6 +25,14 @@ public ConfigFromFileBeforeThisTestExt(String classpathFile) { super(classpathFile); } + /** + * Find the annotated filePath property in the @ConfigFromFileBeforeThisTest annotation on the + * test class. + *

    + * + * @param context + * @return + */ @Override protected String getAnnotationFilePath(ExtensionContext context) { if (context.getElement().isPresent()) { diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java index 790d463c..b261dd18 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java @@ -19,16 +19,21 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Execution(SAME_THREAD) @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFile.properties") +//@TestInstance(Lifecycle.PER_CLASS) // Need to try this class ConfigFromFileMixedUsageTest extends InterceptorTestBase { private static Object coreFoundInTest1; + @BeforeAll + static public void beforeAll() { + System.out.println("ConfigFromFileMixedUsageTest BeforeAll"); + } + @Order(1) @Test - public void test1() throws NoSuchMethodException { + public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); @@ -52,7 +57,7 @@ public void test3() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileBeforeThisTestExt.class, (Class)(this.getClass()), + ConfigFromFileBeforeThisTestExt.class, this, getClass().getMethod("test3", null)); ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); @@ -67,40 +72,153 @@ public void test3() throws NoSuchMethodException { @Order(4) @Test - public void test4() throws NoSuchMethodException { + public void test4() { assertTrue(AndHow.isInitialized()); assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); assertEquals("Bob", Config.MY_PROP.getValue()); } + + /** + * A nested test inherits the ConfigFromFileBeforeAllTests from the parent class, + * BUT IT RE-INSTANTIATES IT! So, AndHow starts over, uninitialized. If any modifications were + * made to the config, it is lost in the inner class. Also, the @BeforeAll of the parent class + * is NOT called again for the Nested class, so it is not safe to modify config in there if using + * inner classes. + */ @Nested @Order(1) + class NestA { + + //static method in inner class not allowed in 1.8 +// @BeforeAll +// static public void beforeAll() { +// System.out.println("NestA BeforeAll"); +// } + + /** + * AndHow is uninitialized, but will initialize to see the result of the parent class's + * @ConfigFromFileBeforeAllTests annotation. + */ + @Test + @Order(1) + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Order(2) + @Test + public void test2() { + assertTrue(AndHow.isInitialized()); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + + @Nested + @Order(1) + class NestAA { + @Test + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + } + + + @Nested + @Order(2) + @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFileNest1.properties") + class NestAB { + @Test + @Order(1) + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + + @Order(2) + @Test + public void test2() { + assertTrue(AndHow.isInitialized()); + assertEquals("BobNest1", Config.MY_PROP.getValue()); + } + } + + + @Nested + @Order(3) + class NestAC { + @Test + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + } + + /** + * Nested and using ConfigFromFileBeforeEachTest + */ + @Nested + @Order(4) + @ConfigFromFileBeforeEachTest(filePath = "ext/MyPropFileNest2.properties") + class NestAD { + + @Test + @Order(1) + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("BobNest2", Config.MY_PROP.getValue()); + } + + @Order(2) + @Test + public void test2() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("BobNest2", Config.MY_PROP.getValue()); + } + } + + @Nested + @Order(5) + class NestAE { + @Test + public void test1() { + assertFalse(AndHow.isInitialized()); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + assertEquals("Bob", Config.MY_PROP.getValue()); + } + } + } + + @Nested + @Order(2) + @ExtendWith(TestInterceptor.class) @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFileNest1.properties") class Nest1 { @Test + @Order(1) public void test1() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); - - ExtensionContext.Namespace expectedNamespace = ExtensionContext.Namespace.create( - ConfigFromFileBeforeAllTestsExt.class, (Class)(this.getClass()), - getClass().getMethod("test1", null)); - - ExtensionContext.Store store = extensionContextDuringTest.getStore(expectedNamespace); - assertNotNull(store); - assertSame(coreFoundInTest1, store.get("core_key"), - "The stored core should be the same one created via BeforeAll in test1 "); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); assertEquals("BobNest1", Config.MY_PROP.getValue()); + + coreFoundInTest1 = AndHowTestUtils.getAndHowCore(); //Only non-null after value access above. } @Order(2) @Test public void test2() { assertTrue(AndHow.isInitialized()); // Single initialization for entire class + assertSame(coreFoundInTest1, AndHowTestUtils.getAndHowCore()); assertEquals("BobNest1", Config.MY_PROP.getValue()); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java index 3a988d15..3626711d 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/InterceptorTestBase.java @@ -8,8 +8,7 @@ * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. */ @ExtendWith(ConfigFromFileBeforeAllTestsExtUsageTest.TestInterceptor.class) -public -class InterceptorTestBase { +public class InterceptorTestBase { protected static ExtensionContext extensionContextDuringTest; From 42ef6db68d5a3c867f626ea03187f2698630ec92 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 3 Nov 2022 22:22:50 -0500 Subject: [PATCH 10/22] Add classesInScope parameter, but not tests use it yet --- .../andhow/testutil/AndHowTestUtils.java | 4 +- .../junit5/ConfigFromFileBeforeAllTests.java | 19 +++ .../junit5/ConfigFromFileBeforeEachTest.java | 18 +++ .../junit5/ConfigFromFileBeforeThisTest.java | 18 +++ .../junit5/ext/ConfigFromFileBaseExt.java | 134 +++++++++++++----- .../ext/ConfigFromFileBeforeAllTestsExt.java | 22 ++- .../ext/ConfigFromFileBeforeEachTestExt.java | 18 ++- .../ext/ConfigFromFileBeforeThisTestExt.java | 13 +- ...nfigFromFileBaseExtDefaultPackageTest.java | 9 +- .../junit5/ext/ConfigFromFileExtUnitTest.java | 16 ++- 10 files changed, 222 insertions(+), 49 deletions(-) diff --git a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java index f7ca3bde..bf974a0c 100644 --- a/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java +++ b/andhow-shared-test-utils/src/main/java/org/yarnandtail/andhow/testutil/AndHowTestUtils.java @@ -204,14 +204,14 @@ public static UnaryOperator setAndHowConfigLocator(UnaryOperator newLo } public static List> setConfigurationOverrideGroups( - Object configurationInstance, List classList) { + Object configurationInstance, List> classList) { return ReflectionTestUtils.setInstanceFieldValue( configurationInstance, "overrideGroups", classList, List.class); } public static List> setConfigurationOverrideGroups( - Object configurationInstance, Class clazz) { + Object configurationInstance, Class clazz) { return setConfigurationOverrideGroups(configurationInstance, Arrays.asList(clazz)); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java index 1f6a35b4..ee8e2877 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java @@ -4,6 +4,7 @@ import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeAllTestsExt; import java.lang.annotation.*; +import java.util.List; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -13,4 +14,22 @@ @ExtendWith(ConfigFromFileBeforeAllTestsExt.class) public @interface ConfigFromFileBeforeAllTests { String filePath(); + + /** + * Optional array of classes that AndHow will limit its inspection to for finding + * AndHow Properties. + *

    + * If you are testing a single class or subset of classes that require + * configuration within a large project with lots of configuration, + * it may make sense to limit AndHow to just the smaller set of classes that + * need configuration for this test or set of tests. + *

    + * If unspecified, all AndHow configuration properties on the classpath + * will be discovered, configured and validated. For a project with lots of + * configuration, that may require lots of configuration unrelated to the test + * in the configured properties file. + * + * @return + */ + Class[] classesInScope() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java index 4cd990ec..bb91a552 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java @@ -14,4 +14,22 @@ @ExtendWith(ConfigFromFileBeforeEachTestExt.class) public @interface ConfigFromFileBeforeEachTest { String filePath(); + + /** + * Optional array of classes that AndHow will limit its inspection to for finding + * AndHow Properties. + *

    + * If you are testing a single class or subset of classes that require + * configuration within a large project with lots of configuration, + * it may make sense to limit AndHow to just the smaller set of classes that + * need configuration for this test or set of tests. + *

    + * If unspecified, all AndHow configuration properties on the classpath + * will be discovered, configured and validated. For a project with lots of + * configuration, that may require lots of configuration unrelated to the test + * in the configured properties file. + * + * @return + */ + Class[] classesInScope() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java index 6123391c..6634fc5d 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java @@ -16,4 +16,22 @@ @ExtendWith(ConfigFromFileBeforeThisTestExt.class) public @interface ConfigFromFileBeforeThisTest { String filePath(); + + /** + * Optional array of classes that AndHow will limit its inspection to for finding + * AndHow Properties. + *

    + * If you are testing a single class or subset of classes that require + * configuration within a large project with lots of configuration, + * it may make sense to limit AndHow to just the smaller set of classes that + * need configuration for this test or set of tests. + *

    + * If unspecified, all AndHow configuration properties on the classpath + * will be discovered, configured and validated. For a project with lots of + * configuration, that may require lots of configuration unrelated to the test + * in the configured properties file. + * + * @return + */ + Class[] classesInScope() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java index 5a5d4c82..23ea8e64 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java @@ -6,7 +6,7 @@ import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.testutil.AndHowTestUtils; -import java.util.List; +import java.util.*; /** * Implementation of an ExtensionBase that configures AndHow from a single properties @@ -31,9 +31,34 @@ */ public abstract class ConfigFromFileBaseExt extends ExtensionBase { + /** Key to store the AndHowCore (if any) of AndHow. */ + protected static final String CORE_KEY = "core_key"; + + /** Key to store the in-process configuration (if any) of the AndHow instance. + * When is the inProcessConfig non-null for AndHow? Only when findConfig() + * has been called but AndHow has not yet initialized. + */ + protected static final String CONFIG_KEY = "config_key"; + /** The complete path to a properties file on the classpath */ private String _classpathFile; + /** + * This Optional has unique usage: + * null: (Optional not initialized) means that this class was constructed via + * the default constructor, which likely means it is being used via an + * annotation. The value for this config param should be discovered at runtime + * from the annotation. + * Optional.isPresent() == false: The value was set to empty in the java + * constructor. This class was constructed w/ a standard constructor that + * spec'ed this value as empty or null. DON'T TRY TO FIND A VALUE IN AN + * ANNOTATION - THERE ISN'T ONE. + * Optional.isPresent() == true: This class was constructed w/ a standard + * constructor that spec'ed this value. DON'T TRY TO FIND A VALUE IN AN + * ANNOTATION - THERE ISN'T ONE. + */ + private Optional[]> _classesInScope; + /** The constructed config instance to be used for AndHow */ protected AndHowConfiguration _config; @@ -46,13 +71,28 @@ public ConfigFromFileBaseExt(String classpathFile) { throw new IllegalArgumentException("The classpath properties file path cannot be null."); } _classpathFile = classpathFile; + _classesInScope = Optional.empty(); + } + + public ConfigFromFileBaseExt(String classpathFile, Class[] configClasses) { + if (classpathFile == null) { + throw new IllegalArgumentException("The classpath properties file path cannot be null."); + } + + _classpathFile = classpathFile; + _classesInScope = Optional.of(configClasses); } /** * Empty constructor used when the \@ConfigFromFile annotation is used. * - * When the empty construct is used, the classpathFile is found via the - * \@ConfigFromFile annotation filePath property. + * When the empty construct is used, the classpathFile and classesInScope are + * found via the \@ConfigFromFile annotation filePath property. + *

    + * This constructor cannot be used to create an instance of this class + * other than via the annotation mechanism. When this constructor is + * used, the class assumes it will find its configuration in an annotation, + * which will not be present other than within the context of a JUnit test. */ public ConfigFromFileBaseExt() { } @@ -61,16 +101,14 @@ public ConfigFromFileBaseExt() { } * that class. * @return */ - protected abstract String getAnnotationFilePath(ExtensionContext context); + protected abstract String getFilePathFromAnnotation(ExtensionContext context); - /** Key to store the AndHowCore (if any) of AndHow. */ - protected static final String CORE_KEY = "core_key"; - - /** Key to store the in-process configuration (if any) of the AndHow instance. - * When is the inProcessConfig non-null for AndHow? Only when findConfig() - * has been called but AndHow has not yet initialized. + /** + * When this Extension is being used in association w/ an annotation, this returns + * the optional configClasses List. + * @return */ - protected static final String CONFIG_KEY = "config_key"; + protected abstract Class[] getClassesInScopeFromAnnotation(ExtensionContext context); /** * Configure AndHow for a unit test class. @@ -89,14 +127,11 @@ public ConfigFromFileBaseExt() { } */ public void beforeAll(ExtensionContext context) throws Exception { - // remove current core and keep to be later restored + // Store the old core and set the current core to null getPerTestClassStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); - String fullPath = expandPath(getClasspathFile(context), context); - verifyClassPath(fullPath); - - // New config instance created just as needed for testing - _config = buildConfig(fullPath); + // New config instance created just as requested for testing + _config = buildConfig(context); // Remove current locator and replace w/ one that always returns a custom config getPerTestClassStore(context).put( @@ -125,13 +160,12 @@ public void afterAll(ExtensionContext context) throws Exception { * @throws Exception */ public void beforeEach(ExtensionContext context) throws Exception { - getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); - String fullPath = expandPath(getClasspathFile(context), context); - verifyClassPath(fullPath); + // Store the old core and set the current core to null + getPerTestMethodStore(context).put(CORE_KEY, AndHowTestUtils.setAndHowCore(null)); - // New config instance created just as needed for testing - _config = buildConfig(fullPath); + // New config instance created just as requested for testing + _config = buildConfig(context); // Remove current locator and replace w/ one that always returns a custom config getPerTestMethodStore(context).put( @@ -168,12 +202,53 @@ public void afterEach(ExtensionContext context) throws Exception { */ protected String getClasspathFile(ExtensionContext context) { if (_classpathFile == null) { - return getAnnotationFilePath(context); + return getFilePathFromAnnotation(context); } else { return _classpathFile; } } + /** + * Find the user configured classes in scope. + *

    + * If configured via an annotation, read the annotated value EVERY TIME, DO NOT CACHE THE VALUE. + * @param context + * @return A List that is never null but may be empty. + */ + protected List> getClassesInScope(ExtensionContext context) { + if (_classesInScope == null) { + //Not initialized, so this is annotation construction + return Arrays.asList(getClassesInScopeFromAnnotation(context)); + } else if (_classesInScope.isPresent()) { + return Arrays.asList(_classesInScope.get()); //Std java class construction was used + } else { + return Collections.emptyList(); + } + } + + /** + * Construct a new AndHowConfiguration instance created as needed for testing + * + * @param context + * @return + */ + protected AndHowConfiguration buildConfig(ExtensionContext context) { + + String fullPath = expandPath(getClasspathFile(context), context); + verifyClassPath(fullPath); + List> clazzes = getClassesInScope(context); + + AndHowConfiguration config = new StdConfig.StdConfigImpl(); + removeEnvLoaders(config); + config.setClasspathPropFilePath(fullPath).classpathPropertiesRequired(); + + if (! clazzes.isEmpty()) { + AndHowTestUtils.setConfigurationOverrideGroups(config, clazzes); + } + + return config; + } + /** * Remove the Loaders that are 'environment' related, meaning they could be set * outside the scope of a unit test. @@ -192,19 +267,6 @@ protected void removeEnvLoaders(AndHowConfiguration buildConfig(String classpathFile) { - AndHowConfiguration config = new StdConfig.StdConfigImpl(); - removeEnvLoaders(config); - config.setClasspathPropFilePath(classpathFile).classpathPropertiesRequired(); - - return config; - } - /** * Expand the passed classpath to be an absolute classpath if it was a relative one. *

    diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java index 6a67c390..030ede25 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java @@ -3,6 +3,9 @@ import org.junit.jupiter.api.extension.*; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeAllTests; +import java.util.Arrays; +import java.util.List; + public class ConfigFromFileBeforeAllTestsExt extends ConfigFromFileBaseExt implements BeforeAllCallback, AfterAllCallback { @@ -36,20 +39,35 @@ public ConfigFromFileBeforeAllTestsExt(String classpathFile) { * @return */ @Override - protected String getAnnotationFilePath(ExtensionContext context) { + protected String getFilePathFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeAllTests cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeAllTests.class); if (cff != null) { return cff.filePath(); } else if (context.getParent().isPresent()) { - return getAnnotationFilePath(context.getParent().get()); + return getFilePathFromAnnotation(context.getParent().get()); } throw new IllegalStateException("Expected the @ConfigFromFileBeforeAllTests annotation on the '" + context.getRequiredTestClass() + "' class or a parent class for a @Nested test."); } + @Override + protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { + ConfigFromFileBeforeAllTests cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeAllTests.class); + + if (cff != null) { + return cff.classesInScope(); + } else if (context.getParent().isPresent()) { + return getClassesInScopeFromAnnotation(context.getParent().get()); + } + + throw new IllegalStateException("Expected the @ConfigFromFileBeforeAllTests annotation on the '" + + context.getRequiredTestClass() + "' class or a parent class for a @Nested test."); + } + + @Override public void beforeAll(final ExtensionContext context) throws Exception { super.beforeAll(context); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java index fd8608e8..80083b4b 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java @@ -37,14 +37,28 @@ public ConfigFromFileBeforeEachTestExt(String classpathFile) { * @return */ @Override - protected String getAnnotationFilePath(ExtensionContext context) { + protected String getFilePathFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeEachTest cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeEachTest.class); if (cff != null) { return cff.filePath(); } else if (context.getParent().isPresent()) { - return getAnnotationFilePath(context.getParent().get()); + return getFilePathFromAnnotation(context.getParent().get()); + } + + throw new IllegalStateException("Expected the @ConfigFromFileBeforeEachTest annotation on the '" + + context.getRequiredTestClass() + "' class or a parent class for a @Nested test."); + } + + @Override + protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { + ConfigFromFileBeforeEachTest cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeEachTest.class); + + if (cff != null) { + return cff.classesInScope(); + } else if (context.getParent().isPresent()) { + return getClassesInScopeFromAnnotation(context.getParent().get()); } throw new IllegalStateException("Expected the @ConfigFromFileBeforeEachTest annotation on the '" + diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java index 5c5a36b4..08e86893 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java @@ -34,7 +34,7 @@ public ConfigFromFileBeforeThisTestExt(String classpathFile) { * @return */ @Override - protected String getAnnotationFilePath(ExtensionContext context) { + protected String getFilePathFromAnnotation(ExtensionContext context) { if (context.getElement().isPresent()) { ConfigFromFileBeforeThisTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeThisTest.class); return cff.filePath(); @@ -44,6 +44,17 @@ protected String getAnnotationFilePath(ExtensionContext context) { } } + @Override + protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { + if (context.getElement().isPresent()) { + ConfigFromFileBeforeThisTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeThisTest.class); + return cff.classesInScope(); + } else { + throw new IllegalStateException("Expected the @ConfigFromFileBeforeThisTest annotation on the '" + + context.getRequiredTestMethod() + "' test method."); + } + } + @Override public void beforeEach(final ExtensionContext context) throws Exception { super.beforeEach(context); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java index b70e4810..f3ea03a6 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/ConfigFromFileBaseExtDefaultPackageTest.java @@ -90,14 +90,21 @@ public void expandPathShouldNotExpandAbsPaths() { /* Simple subclass to test protected methods */ public static class ConfigFromFileBaseExtSimple extends ConfigFromFileBaseExt { + + // Only works in limited contexts where the full class is not being invoked public ConfigFromFileBaseExtSimple() { super(""); } - public String getAnnotationFilePath(ExtensionContext context) { + public String getFilePathFromAnnotation(ExtensionContext context) { return ""; } + @Override + protected Class[] getClassesInScopeFromAnnotation(final ExtensionContext context) { + return new Class[0]; + } + public String expandPath(String classpath, ExtensionContext context) { return super.expandPath(classpath, context); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java index a600daec..32ed92e2 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java @@ -120,7 +120,7 @@ void completeWorkflowWithNonNullCoreAndNullConfig() throws Exception { String cp = "MyPropFile.properties"; - ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); + ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp, new Class[] {this.getClass()}); // The initial event called on extension by JUnit theExt.beforeAll(extensionContext); @@ -220,7 +220,7 @@ void completeWorkflowWithNullCoreAndNonNullConfig() throws Exception { String cp = "MyPropFile.properties"; - ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp); + ConfigFromFileExtSimple theExt = new ConfigFromFileExtSimple(cp, new Class[] {this.getClass()}); // The initial event called on extension by JUnit theExt.beforeAll(extensionContext); @@ -287,15 +287,21 @@ void completeWorkflowWithNullCoreAndNonNullConfig() throws Exception { /* Simple subclass to test protected methods */ public static class ConfigFromFileExtSimple extends ConfigFromFileBaseExt { + // Only works in limited contexts where the full class is not being invoked public ConfigFromFileExtSimple() { super(""); } @Override - protected String getAnnotationFilePath(final ExtensionContext context) { return null; } + protected String getFilePathFromAnnotation(final ExtensionContext context) { return null; } - public ConfigFromFileExtSimple(String classpathFile) { - super(classpathFile); + @Override + protected Class[] getClassesInScopeFromAnnotation(final ExtensionContext context) { + return new Class[0]; + } + + public ConfigFromFileExtSimple(String classpathFile, Class[] classesInScope) { + super(classpathFile, classesInScope); } public static String getCoreKey() { From 3bbb4563ce7dcd1d666174fb85ff2225645a0459 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Sat, 5 Nov 2022 21:41:13 -0500 Subject: [PATCH 11/22] Add ability to specify scope of classes for tests --- .../junit5/ext/ConfigFromFileBaseExt.java | 30 +++++++++- .../org/yarnandtail/andhow/junit5/Conf1.java | 12 ++++ .../org/yarnandtail/andhow/junit5/Conf2.java | 13 +++++ ...ava => ConfigFromFileMixedUsage1Test.java} | 29 ++++------ .../junit5/ConfigFromFileMixedUsage2Test.java | 58 +++++++++++++++++++ .../andhow/junit5/Conf1And2AsBob.properties | 4 ++ .../andhow/junit5/Conf1And2AsCarl.properties | 4 ++ .../andhow/junit5/Conf1OnlyAsDeb.properties | 2 + .../andhow/junit5/MyPropFile2.properties | 1 + .../andhow/junit5/MyPropFileNest1.properties | 1 + .../andhow/junit5/MyPropFileNest2.properties | 1 + 11 files changed, 136 insertions(+), 19 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf1.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/{ConfigFromFileMixedUsageTest.java => ConfigFromFileMixedUsage1Test.java} (90%) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsBob.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsCarl.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1OnlyAsDeb.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFile2.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest1.properties create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest2.properties diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java index 23ea8e64..8c1d9ef8 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java @@ -6,6 +6,7 @@ import org.yarnandtail.andhow.load.std.*; import org.yarnandtail.andhow.testutil.AndHowTestUtils; +import java.lang.reflect.Modifier; import java.util.*; /** @@ -218,14 +219,39 @@ protected String getClasspathFile(ExtensionContext context) { protected List> getClassesInScope(ExtensionContext context) { if (_classesInScope == null) { //Not initialized, so this is annotation construction - return Arrays.asList(getClassesInScopeFromAnnotation(context)); + return stem(Arrays.asList(getClassesInScopeFromAnnotation(context))); } else if (_classesInScope.isPresent()) { - return Arrays.asList(_classesInScope.get()); //Std java class construction was used + return stem(Arrays.asList(_classesInScope.get())); //Std java class construction was used } else { return Collections.emptyList(); } } + /** + * Generate a comprehensive list of classes that includes inner class which might contain + * AndHow properties. + */ + protected List> stem(List> clazzes) { + final List> stemmed = new ArrayList<>(); + stemmed.addAll(clazzes); + + int index = 0; + + while (index < stemmed.size()) { + Class c = stemmed.get(index); + + Arrays.stream(c.getDeclaredClasses()).forEach( cc -> { + if (Modifier.isStatic(cc.getModifiers())) { + stemmed.add(cc); + } + }); + + index++; + } + + return stemmed; + } + /** * Construct a new AndHowConfiguration instance created as needed for testing * diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf1.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf1.java new file mode 100644 index 00000000..e5a682ee --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf1.java @@ -0,0 +1,12 @@ +package org.yarnandtail.andhow.junit5; + +import org.yarnandtail.andhow.property.StrProp; + +public interface Conf1 { + + StrProp MY_PROP = StrProp.builder().aliasInAndOut("CONF1_MY_PROP").build(); + + static interface Inner1 { + StrProp MY_PROP = StrProp.builder().aliasInAndOut("CONF1_INNER1_MY_PROP").build(); + } +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java new file mode 100644 index 00000000..cbb2c869 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java @@ -0,0 +1,13 @@ +package org.yarnandtail.andhow.junit5; + +import org.yarnandtail.andhow.property.StrProp; + +public interface Conf2 { + + StrProp MY_PROP = StrProp.builder().aliasInAndOut("CONF2.MY.PROP").build(); + + static interface Inner1 { + // Required! + StrProp MY_PROP = StrProp.builder().aliasInAndOut("CONF2_INNER1_MY_PROP").notNull().build(); + } +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java similarity index 90% rename from junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java rename to junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java index b261dd18..6ebca961 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsageTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java @@ -20,21 +20,16 @@ @Execution(SAME_THREAD) @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFile.properties") //@TestInstance(Lifecycle.PER_CLASS) // Need to try this -class ConfigFromFileMixedUsageTest extends InterceptorTestBase { +class ConfigFromFileMixedUsage1Test extends InterceptorTestBase { private static Object coreFoundInTest1; - @BeforeAll - static public void beforeAll() { - System.out.println("ConfigFromFileMixedUsageTest BeforeAll"); - } - @Order(1) @Test public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); @@ -65,7 +60,7 @@ public void test3() throws NoSuchMethodException { assertSame(coreFoundInTest1, store.get("core_key"), "The stored core should be the same one created via BeforeAll in test1 "); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob2", Config.MY_PROP.getValue()); } @@ -104,7 +99,7 @@ class NestA { @Order(1) public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); } @@ -121,7 +116,7 @@ class NestAA { @Test public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); } } @@ -135,7 +130,7 @@ class NestAB { @Order(1) public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("BobNest1", Config.MY_PROP.getValue()); } @@ -154,7 +149,7 @@ class NestAC { @Test public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); } } @@ -171,7 +166,7 @@ class NestAD { @Order(1) public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("BobNest2", Config.MY_PROP.getValue()); } @@ -179,7 +174,7 @@ public void test1() { @Test public void test2() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("BobNest2", Config.MY_PROP.getValue()); } } @@ -190,7 +185,7 @@ class NestAE { @Test public void test1() { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob", Config.MY_PROP.getValue()); } } @@ -207,7 +202,7 @@ class Nest1 { public void test1() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("BobNest1", Config.MY_PROP.getValue()); @@ -238,7 +233,7 @@ public void test3() throws NoSuchMethodException { assertNotNull(store); assertNotSame(coreFoundInTest1, store.get("core_key")); - AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsageTest.Config.class); + AndHowTestUtils.setConfigurationOverrideGroups(AndHow.findConfig(), ConfigFromFileMixedUsage1Test.Config.class); assertEquals("Bob2", Config.MY_PROP.getValue()); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java new file mode 100644 index 00000000..8e466ae0 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java @@ -0,0 +1,58 @@ +package org.yarnandtail.andhow.junit5; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.parallel.Execution; +import org.yarnandtail.andhow.AndHow; +import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeThisTestExt; +import org.yarnandtail.andhow.junit5.ext.InterceptorTestBase; +import org.yarnandtail.andhow.property.StrProp; +import org.yarnandtail.andhow.testutil.AndHowTestUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; + +/** + * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext + * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. + */ +@TestClassOrder(ClassOrderer.OrderAnnotation.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Execution(SAME_THREAD) +@ConfigFromFileBeforeAllTests(filePath = "Conf1And2AsBob.properties", classesInScope = {Conf1.class, Conf2.class}) +//@TestInstance(Lifecycle.PER_CLASS) // Need to try this +class ConfigFromFileMixedUsage2Test { + + @Order(1) + @Test + public void test1() { + assertEquals("Bob", Conf1.MY_PROP.getValue()); + assertEquals("Bob", Conf1.Inner1.MY_PROP.getValue()); + assertEquals("Bob", Conf2.MY_PROP.getValue()); + assertEquals("Bob", Conf2.Inner1.MY_PROP.getValue()); + } + + @Order(2) + @Test + @ConfigFromFileBeforeThisTest(filePath = "Conf1OnlyAsDeb.properties", classesInScope = {Conf1.class}) + public void test2() throws NoSuchMethodException { + + assertFalse(AndHow.isInitialized()); + + assertEquals("Deb", Conf1.MY_PROP.getValue()); + assertEquals("Deb", Conf1.Inner1.MY_PROP.getValue()); + assertThrows(RuntimeException.class, () -> Conf2.MY_PROP.getValue()); + assertThrows(RuntimeException.class, () -> Conf2.Inner1.MY_PROP.getValue()); + } + + @Order(3) + @Test + public void test3() { + assertEquals("Bob", Conf1.MY_PROP.getValue()); + assertEquals("Bob", Conf1.Inner1.MY_PROP.getValue()); + assertEquals("Bob", Conf2.MY_PROP.getValue()); + assertEquals("Bob", Conf2.Inner1.MY_PROP.getValue()); + } + +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsBob.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsBob.properties new file mode 100644 index 00000000..830b9c93 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsBob.properties @@ -0,0 +1,4 @@ +CONF1_MY_PROP: Bob +CONF1_INNER1_MY_PROP: Bob +CONF2.MY.PROP: Bob +CONF2_INNER1_MY_PROP: Bob \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsCarl.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsCarl.properties new file mode 100644 index 00000000..380ab5d4 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1And2AsCarl.properties @@ -0,0 +1,4 @@ +CONF1_MY_PROP: Carl +CONF1_INNER1_MY_PROP: Carl +CONF2_MY_PROP: Carl +CONF2_INNER1_MY_PROP: Carl \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1OnlyAsDeb.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1OnlyAsDeb.properties new file mode 100644 index 00000000..c3533e12 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/Conf1OnlyAsDeb.properties @@ -0,0 +1,2 @@ +CONF1_MY_PROP: Deb +CONF1_INNER1_MY_PROP: Deb \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFile2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFile2.properties new file mode 100644 index 00000000..3091981b --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFile2.properties @@ -0,0 +1 @@ +MY_PROP: Bob2 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest1.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest1.properties new file mode 100644 index 00000000..33f9012a --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest1.properties @@ -0,0 +1 @@ +MY_PROP: BobNest1 \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest2.properties b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest2.properties new file mode 100644 index 00000000..a994333c --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/resources/org/yarnandtail/andhow/junit5/MyPropFileNest2.properties @@ -0,0 +1 @@ +MY_PROP: BobNest2 \ No newline at end of file From 6dcaba6185721096556b4c144705242cef1f4db7 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Tue, 8 Nov 2022 10:44:53 -0600 Subject: [PATCH 12/22] Added test and cleanup --- .../junit5/ConfigFromFileMixedUsage1Test.java | 1 - .../junit5/ConfigFromFileMixedUsage2Test.java | 34 ++++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java index 6ebca961..b5adec64 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java @@ -19,7 +19,6 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Execution(SAME_THREAD) @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFile.properties") -//@TestInstance(Lifecycle.PER_CLASS) // Need to try this class ConfigFromFileMixedUsage1Test extends InterceptorTestBase { private static Object coreFoundInTest1; diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java index 8e466ae0..2ebebbfc 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java @@ -1,14 +1,11 @@ package org.yarnandtail.andhow.junit5; import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.parallel.Execution; import org.yarnandtail.andhow.AndHow; -import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeThisTestExt; -import org.yarnandtail.andhow.junit5.ext.InterceptorTestBase; -import org.yarnandtail.andhow.property.StrProp; -import org.yarnandtail.andhow.testutil.AndHowTestUtils; +import org.yarnandtail.andhow.api.AppFatalException; +import org.yarnandtail.andhow.api.Problem; +import org.yarnandtail.andhow.internal.RequirementProblem; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; @@ -21,12 +18,12 @@ @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Execution(SAME_THREAD) @ConfigFromFileBeforeAllTests(filePath = "Conf1And2AsBob.properties", classesInScope = {Conf1.class, Conf2.class}) -//@TestInstance(Lifecycle.PER_CLASS) // Need to try this class ConfigFromFileMixedUsage2Test { @Order(1) @Test public void test1() { + assertFalse(AndHow.isInitialized(), "Shouldn't be init before 1st test"); assertEquals("Bob", Conf1.MY_PROP.getValue()); assertEquals("Bob", Conf1.Inner1.MY_PROP.getValue()); assertEquals("Bob", Conf2.MY_PROP.getValue()); @@ -38,7 +35,7 @@ public void test1() { @ConfigFromFileBeforeThisTest(filePath = "Conf1OnlyAsDeb.properties", classesInScope = {Conf1.class}) public void test2() throws NoSuchMethodException { - assertFalse(AndHow.isInitialized()); + assertFalse(AndHow.isInitialized(), "Shouldn't be init bc this test forces a new config"); assertEquals("Deb", Conf1.MY_PROP.getValue()); assertEquals("Deb", Conf1.Inner1.MY_PROP.getValue()); @@ -48,7 +45,26 @@ public void test2() throws NoSuchMethodException { @Order(3) @Test - public void test3() { + @ConfigFromFileBeforeThisTest(filePath = "Conf1OnlyAsDeb.properties", classesInScope = {Conf1.class, Conf2.class}) + public void puttingUnconfiguredRequiredPropertiesInScopeShouldError() { + + assertFalse(AndHow.isInitialized(), "Shouldn't be init bc this test forces a new config"); + + AppFatalException e = assertThrows(AppFatalException.class, () -> Conf1.MY_PROP.getValue()); + + assertEquals(1, e.getProblems().size()); + Problem p = e.getProblems().get(0); + assertTrue(p instanceof RequirementProblem.NonNullPropertyProblem); + RequirementProblem.NonNullPropertyProblem nnp = (RequirementProblem.NonNullPropertyProblem)p; + assertSame(Conf2.Inner1.MY_PROP, nnp.getPropertyCoord().getProperty()); + } + + @Order(4) + @Test + public void test4() { + + assertTrue(AndHow.isInitialized(), "Should be init bc the config state should be restore to post-test1"); + assertEquals("Bob", Conf1.MY_PROP.getValue()); assertEquals("Bob", Conf1.Inner1.MY_PROP.getValue()); assertEquals("Bob", Conf2.MY_PROP.getValue()); From 080e0f3cc938c8f705617165b81c478b5072f489 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Tue, 8 Nov 2022 23:00:21 -0600 Subject: [PATCH 13/22] small renaming refactor and javadocs --- .../junit5/ConfigFromFileBeforeAllTests.java | 53 +++++++++++++----- .../junit5/ConfigFromFileBeforeEachTest.java | 52 +++++++++++++----- .../junit5/ConfigFromFileBeforeThisTest.java | 54 +++++++++++++------ .../ext/ConfigFromFileBeforeAllTestsExt.java | 7 +-- .../ext/ConfigFromFileBeforeEachTestExt.java | 5 +- .../ext/ConfigFromFileBeforeThisTestExt.java | 5 +- .../junit5/ConfigFromFileMixedUsage1Test.java | 12 ++--- .../junit5/ConfigFromFileMixedUsage2Test.java | 14 ++--- .../junit5/ext/ConfigFromFileExtUnitTest.java | 3 ++ 9 files changed, 136 insertions(+), 69 deletions(-) diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java index ee8e2877..dad6e069 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeAllTests.java @@ -4,7 +4,6 @@ import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeAllTestsExt; import java.lang.annotation.*; -import java.util.List; import static java.lang.annotation.ElementType.*; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -13,23 +12,49 @@ @Retention(RUNTIME) @ExtendWith(ConfigFromFileBeforeAllTestsExt.class) public @interface ConfigFromFileBeforeAllTests { - String filePath(); /** - * Optional array of classes that AndHow will limit its inspection to for finding - * AndHow Properties. + * The classpath to a properties file containing the {@code property_name : value} entries + * to configure AndHow Property values before the run of this test class. + *

    + * If the path starts with a '{@code / }', it is interpreted as an absolute classpath. + * If the path does not start with a '{@code / }', it is interpreted as relative to the current + * test class. Some examples: + *

      + *
    • /myFile.props - The file 'myFile.props' expected at the root of the classpath
    • + *
    • /sub/myFile.props - The file 'myFile.props' expected in the 'sub' package or directory of the classpath
    • + *
    • myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people package on the classpath
    • + *
    • sub/myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people.sub package on the classpath
    • + *
    + *

    + * If specifying only the configuration properties file, the shortened annotation syntax can be used:
    + * {@code @ConfigFromFileBeforeAllTests("myFile.props")}
    + * Otherwise the full syntax must be used:
    + * {@code @ConfigFromFileBeforeAllTests(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}}
    + *

    + * @return A string containing the path to a properties file. + */ + String value(); + + /** + * Optional array of classes that AndHow will scan for AndHow Properties. *

    - * If you are testing a single class or subset of classes that require - * configuration within a large project with lots of configuration, - * it may make sense to limit AndHow to just the smaller set of classes that - * need configuration for this test or set of tests. + * If testing a single class or subset of classes that use AndHow Properties within a larger + * project, you can limit the scope of which Properties AndHow will 'see' to just those needed + * for the test. All Properties in the listed classes and nested innerclasses will be scanned + * for AndHow Properties, but no others. This reduces the size of the properties files + * required for configuration and limits the effects of refactors on Property names. *

    * If unspecified, all AndHow configuration properties on the classpath - * will be discovered, configured and validated. For a project with lots of - * configuration, that may require lots of configuration unrelated to the test - * in the configured properties file. - * - * @return + * will be discovered, configured and validated. Thus, any configuration Properties that + * are required to be non-null will need to have values provided via the configured properties + * file or in some other way. + *

    + * Typical usage looks like this:
    + * {@code @ConfigFromFileBeforeAllTests(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}} + *

    + * @return An array of classes that should be scanned (along with their nested innerclasses) for + * AndHow Properties. */ - Class[] classesInScope() default {}; + Class[] includeClasses() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java index bb91a552..a806e59f 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeEachTest.java @@ -13,23 +13,49 @@ @Inherited @ExtendWith(ConfigFromFileBeforeEachTestExt.class) public @interface ConfigFromFileBeforeEachTest { - String filePath(); /** - * Optional array of classes that AndHow will limit its inspection to for finding - * AndHow Properties. + * The classpath to a properties file containing the {@code property_name : value} entries + * to configure AndHow Property values before the run of each test in this class. + *

    + * If the path starts with a '{@code / }', it is interpreted as an absolute classpath. + * If the path does not start with a '{@code / }', it is interpreted as relative to the current + * test class. Some examples: + *

      + *
    • /myFile.props - The file 'myFile.props' expected at the root of the classpath
    • + *
    • /sub/myFile.props - The file 'myFile.props' expected in the 'sub' package or directory of the classpath
    • + *
    • myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people package on the classpath
    • + *
    • sub/myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people.sub package on the classpath
    • + *
    + *

    + * If specifying only the configuration properties file, the shortened annotation syntax can be used:
    + * {@code @ConfigFromFileBeforeEachTest("myFile.props")}
    + * Otherwise the full syntax must be used:
    + * {@code @ConfigFromFileBeforeEachTest(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}}
    + *

    + * @return A string containing the path to a properties file. + */ + String value(); + + /** + * Optional array of classes that AndHow will scan for AndHow Properties. *

    - * If you are testing a single class or subset of classes that require - * configuration within a large project with lots of configuration, - * it may make sense to limit AndHow to just the smaller set of classes that - * need configuration for this test or set of tests. + * If testing a single class or subset of classes that use AndHow Properties within a larger + * project, you can limit the scope of which Properties AndHow will 'see' to just those needed + * for the test. All Properties in the listed classes and nested innerclasses will be scanned + * for AndHow Properties, but no others. This reduces the size of the properties files + * required for configuration and limits the effects of refactors on Property names. *

    * If unspecified, all AndHow configuration properties on the classpath - * will be discovered, configured and validated. For a project with lots of - * configuration, that may require lots of configuration unrelated to the test - * in the configured properties file. - * - * @return + * will be discovered, configured and validated. Thus, any configuration Properties that + * are required to be non-null will need to have values provided via the configured properties + * file or in some other way. + *

    + * Typical usage looks like this:
    + * {@code @ConfigFromFileBeforeEachTest(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}} + *

    + * @return An array of classes that should be scanned (along with their nested innerclasses) for + * AndHow Properties. */ - Class[] classesInScope() default {}; + Class[] includeClasses() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java index 6634fc5d..72198a28 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ConfigFromFileBeforeThisTest.java @@ -1,8 +1,6 @@ package org.yarnandtail.andhow.junit5; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBaseExt; import org.yarnandtail.andhow.junit5.ext.ConfigFromFileBeforeThisTestExt; import java.lang.annotation.*; @@ -15,23 +13,49 @@ @Inherited @ExtendWith(ConfigFromFileBeforeThisTestExt.class) public @interface ConfigFromFileBeforeThisTest { - String filePath(); /** - * Optional array of classes that AndHow will limit its inspection to for finding - * AndHow Properties. + * The classpath to a properties file containing the {@code property_name : value} entries + * to configure AndHow Property values before the run of this test. + *

    + * If the path starts with a '{@code / }', it is interpreted as an absolute classpath. + * If the path does not start with a '{@code / }', it is interpreted as relative to the current + * test class. Some examples: + *

      + *
    • /myFile.props - The file 'myFile.props' expected at the root of the classpath
    • + *
    • /sub/myFile.props - The file 'myFile.props' expected in the 'sub' package or directory of the classpath
    • + *
    • myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people package on the classpath
    • + *
    • sub/myFile.props (for a test in the org.people package) - The file 'myFile.props' expected in the org.people.sub package on the classpath
    • + *
    + *

    + * If specifying only the configuration properties file, the shortened annotation syntax can be used:
    + * {@code @ConfigFromFileBeforeThisTest("myFile.props")}
    + * Otherwise the full syntax must be used:
    + * {@code @ConfigFromFileBeforeThisTest(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}}
    + *

    + * @return A string containing the path to a properties file. + */ + String value(); + + /** + * Optional array of classes that AndHow will scan for AndHow Properties. *

    - * If you are testing a single class or subset of classes that require - * configuration within a large project with lots of configuration, - * it may make sense to limit AndHow to just the smaller set of classes that - * need configuration for this test or set of tests. + * If testing a single class or subset of classes that use AndHow Properties within a larger + * project, you can limit the scope of which Properties AndHow will 'see' to just those needed + * for the test. All Properties in the listed classes and nested innerclasses will be scanned + * for AndHow Properties, but no others. This reduces the size of the properties files + * required for configuration and limits the effects of refactors on Property names. *

    * If unspecified, all AndHow configuration properties on the classpath - * will be discovered, configured and validated. For a project with lots of - * configuration, that may require lots of configuration unrelated to the test - * in the configured properties file. - * - * @return + * will be discovered, configured and validated. Thus, any configuration Properties that + * are required to be non-null will need to have values provided via the configured properties + * file or in some other way. + *

    + * Typical usage looks like this:
    + * {@code @ConfigFromFileBeforeThisTest(value = "myFile.props"), includeClasses = {Conf1.class, Conf2.class}} + *

    + * @return An array of classes that should be scanned (along with their nested innerclasses) for + * AndHow Properties. */ - Class[] classesInScope() default {}; + Class[] includeClasses() default {}; } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java index 030ede25..e02b3614 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExt.java @@ -3,9 +3,6 @@ import org.junit.jupiter.api.extension.*; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeAllTests; -import java.util.Arrays; -import java.util.List; - public class ConfigFromFileBeforeAllTestsExt extends ConfigFromFileBaseExt implements BeforeAllCallback, AfterAllCallback { @@ -44,7 +41,7 @@ protected String getFilePathFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeAllTests cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeAllTests.class); if (cff != null) { - return cff.filePath(); + return cff.value(); } else if (context.getParent().isPresent()) { return getFilePathFromAnnotation(context.getParent().get()); } @@ -58,7 +55,7 @@ protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeAllTests cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeAllTests.class); if (cff != null) { - return cff.classesInScope(); + return cff.includeClasses(); } else if (context.getParent().isPresent()) { return getClassesInScopeFromAnnotation(context.getParent().get()); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java index 80083b4b..0d57a62c 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExt.java @@ -1,7 +1,6 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.extension.*; -import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeAllTests; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeEachTest; public class ConfigFromFileBeforeEachTestExt extends ConfigFromFileBaseExt @@ -42,7 +41,7 @@ protected String getFilePathFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeEachTest cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeEachTest.class); if (cff != null) { - return cff.filePath(); + return cff.value(); } else if (context.getParent().isPresent()) { return getFilePathFromAnnotation(context.getParent().get()); } @@ -56,7 +55,7 @@ protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { ConfigFromFileBeforeEachTest cff = context.getRequiredTestClass().getAnnotation(ConfigFromFileBeforeEachTest.class); if (cff != null) { - return cff.classesInScope(); + return cff.includeClasses(); } else if (context.getParent().isPresent()) { return getClassesInScopeFromAnnotation(context.getParent().get()); } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java index 08e86893..51921470 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExt.java @@ -1,7 +1,6 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.extension.*; -import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeEachTest; import org.yarnandtail.andhow.junit5.ConfigFromFileBeforeThisTest; public class ConfigFromFileBeforeThisTestExt extends ConfigFromFileBaseExt @@ -37,7 +36,7 @@ public ConfigFromFileBeforeThisTestExt(String classpathFile) { protected String getFilePathFromAnnotation(ExtensionContext context) { if (context.getElement().isPresent()) { ConfigFromFileBeforeThisTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeThisTest.class); - return cff.filePath(); + return cff.value(); } else { throw new IllegalStateException("Expected the @ConfigFromFileBeforeThisTest annotation on the '" + context.getRequiredTestMethod() + "' test method."); @@ -48,7 +47,7 @@ protected String getFilePathFromAnnotation(ExtensionContext context) { protected Class[] getClassesInScopeFromAnnotation(ExtensionContext context) { if (context.getElement().isPresent()) { ConfigFromFileBeforeThisTest cff = context.getElement().get().getAnnotation(ConfigFromFileBeforeThisTest.class); - return cff.classesInScope(); + return cff.includeClasses(); } else { throw new IllegalStateException("Expected the @ConfigFromFileBeforeThisTest annotation on the '" + context.getRequiredTestMethod() + "' test method."); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java index b5adec64..a21eb4ac 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java @@ -18,7 +18,7 @@ @TestClassOrder(ClassOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Execution(SAME_THREAD) -@ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFile.properties") +@ConfigFromFileBeforeAllTests(value = "ext/MyPropFile.properties") class ConfigFromFileMixedUsage1Test extends InterceptorTestBase { private static Object coreFoundInTest1; @@ -45,7 +45,7 @@ public void test2() { @Order(3) @Test - @ConfigFromFileBeforeThisTest(filePath = "ext/MyPropFile2.properties") + @ConfigFromFileBeforeThisTest(value = "ext/MyPropFile2.properties") public void test3() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); @@ -123,7 +123,7 @@ public void test1() { @Nested @Order(2) - @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFileNest1.properties") + @ConfigFromFileBeforeAllTests(value = "ext/MyPropFileNest1.properties") class NestAB { @Test @Order(1) @@ -158,7 +158,7 @@ public void test1() { */ @Nested @Order(4) - @ConfigFromFileBeforeEachTest(filePath = "ext/MyPropFileNest2.properties") + @ConfigFromFileBeforeEachTest(value = "ext/MyPropFileNest2.properties") class NestAD { @Test @@ -193,7 +193,7 @@ public void test1() { @Nested @Order(2) @ExtendWith(TestInterceptor.class) - @ConfigFromFileBeforeAllTests(filePath = "ext/MyPropFileNest1.properties") + @ConfigFromFileBeforeAllTests(value = "ext/MyPropFileNest1.properties") class Nest1 { @Test @@ -219,7 +219,7 @@ public void test2() { @Order(3) @Test - @ConfigFromFileBeforeThisTest(filePath = "ext/MyPropFile2.properties") + @ConfigFromFileBeforeThisTest(value = "ext/MyPropFile2.properties") public void test3() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java index 2ebebbfc..030de6e7 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java @@ -1,23 +1,17 @@ package org.yarnandtail.andhow.junit5; import org.junit.jupiter.api.*; -import org.junit.jupiter.api.parallel.Execution; import org.yarnandtail.andhow.AndHow; import org.yarnandtail.andhow.api.AppFatalException; import org.yarnandtail.andhow.api.Problem; import org.yarnandtail.andhow.internal.RequirementProblem; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD; -/** - * This class shares a static var 'extensionContextDuringTest' which is the JUnit ExtensionContext - * used during a test. Multiple threads executing the test would break this, thus SAME_THREAD. - */ + @TestClassOrder(ClassOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@Execution(SAME_THREAD) -@ConfigFromFileBeforeAllTests(filePath = "Conf1And2AsBob.properties", classesInScope = {Conf1.class, Conf2.class}) +@ConfigFromFileBeforeAllTests(value = "Conf1And2AsBob.properties", includeClasses = {Conf1.class, Conf2.class}) class ConfigFromFileMixedUsage2Test { @Order(1) @@ -32,7 +26,7 @@ public void test1() { @Order(2) @Test - @ConfigFromFileBeforeThisTest(filePath = "Conf1OnlyAsDeb.properties", classesInScope = {Conf1.class}) + @ConfigFromFileBeforeThisTest(value = "Conf1OnlyAsDeb.properties", includeClasses = {Conf1.class}) public void test2() throws NoSuchMethodException { assertFalse(AndHow.isInitialized(), "Shouldn't be init bc this test forces a new config"); @@ -45,7 +39,7 @@ public void test2() throws NoSuchMethodException { @Order(3) @Test - @ConfigFromFileBeforeThisTest(filePath = "Conf1OnlyAsDeb.properties", classesInScope = {Conf1.class, Conf2.class}) + @ConfigFromFileBeforeThisTest(value = "Conf1OnlyAsDeb.properties", includeClasses = {Conf1.class, Conf2.class}) public void puttingUnconfiguredRequiredPropertiesInScopeShouldError() { assertFalse(AndHow.isInitialized(), "Shouldn't be init bc this test forces a new config"); diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java index 32ed92e2..1e87236b 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileExtUnitTest.java @@ -79,6 +79,9 @@ public void expandPathShouldReturnPackageOfContainingClassForInnerClasses() { assertEquals("/org/yarnandtail/andhow/junit5/ext/myFile.props", ext.expandPath("myFile.props", extensionContext)); + + assertEquals("/org/yarnandtail/andhow/junit5/ext/subpkg/myFile.props", + ext.expandPath("subpkg/myFile.props", extensionContext)); } /* NOTE: Testing building correct paths with the default package are handled From bda9c45897e60e3627343972a8b580244188f752 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 9 Nov 2022 13:02:56 -0600 Subject: [PATCH 14/22] Added testing --- .../andhow/junit5/ext/ExtensionBase.java | 2 +- .../andhow/junit5/ext/ExtensionBaseTest.java | 51 ++++++++++++++++++- .../org/yarnandtail/andhow/junit5/Conf2.java | 4 ++ .../org/yarnandtail/andhow/junit5/Conf3.java | 5 ++ .../junit5/ConfigFromFileMixedUsage1Test.java | 6 +-- .../junit5/ConfigFromFileMixedUsage2Test.java | 2 +- 6 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf3.java diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java index 5cfc0829..336a07b3 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.extension.*; -public class ExtensionBase { +public abstract class ExtensionBase { /** * Create or return a unique storage space, which is unique per the test class. diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java index 11c8db92..65ba2caa 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java @@ -2,21 +2,56 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.*; class ExtensionBaseTest { + SimpleExtensionBase extBase; + + ExtensionContext.Store store; + + //The context object that is passed to the test extension + ExtensionContext extensionContext; + @BeforeEach - void setUp() { + public void setUp() throws NoSuchMethodException { + + + // + // Setup mockito for the test + + extBase = new SimpleExtensionBase(); + + store = Mockito.mock(ExtensionContext.Store.class); + + extensionContext = Mockito.mock(ExtensionContext.class); + Mockito.when(extensionContext.getRequiredTestClass()).thenReturn((Class)(this.getClass())); + Mockito.when(extensionContext.getRequiredTestInstance()).thenReturn(this); + Mockito.when(extensionContext.getRequiredTestMethod()).thenReturn(this.getClass().getMethod("setUp", null)); + Mockito.when(extensionContext.getStore(ArgumentMatchers.any())).thenReturn(store); } @Test void getPerTestClassStore() { } + /** + * This test is VERIFYING THE WRONG BEHAVIOR!! + * There is a separate ticket to fix it: https://github.com/eeverman/andhow/issues/744 + */ @Test void getPerTestNamespace() { + ExtensionContext.Namespace sut = extBase.getPerTestNamespace(extensionContext); + + ExtensionContext.Namespace ref = ExtensionContext.Namespace.create( + SimpleExtensionBase.class, this.getClass() + ); + + assertEquals(ref, sut); } @Test @@ -24,6 +59,18 @@ void getPerTestMethodStore() { } @Test - void getPerTestMethodNamespace() { + void getPerTestMethodNamespace() throws NoSuchMethodException { + ExtensionContext.Namespace sut = extBase.getPerTestMethodNamespace(extensionContext); + + ExtensionContext.Namespace ref = ExtensionContext.Namespace.create( + SimpleExtensionBase.class, this, this.getClass().getMethod("setUp", null) + ); + + assertEquals(ref, sut); + } + + // Simple implementation to create a real subclass of ExtensionBase. + static class SimpleExtensionBase extends ExtensionBase { + } } \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java index cbb2c869..762dd621 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf2.java @@ -10,4 +10,8 @@ static interface Inner1 { // Required! StrProp MY_PROP = StrProp.builder().aliasInAndOut("CONF2_INNER1_MY_PROP").notNull().build(); } + + static interface Inner2 { + // I have no config properties... + } } diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf3.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf3.java new file mode 100644 index 00000000..0f637436 --- /dev/null +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/Conf3.java @@ -0,0 +1,5 @@ +package org.yarnandtail.andhow.junit5; + +public interface Conf3 { + // I have no configuration properties... +} diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java index a21eb4ac..51cef1b3 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage1Test.java @@ -18,7 +18,7 @@ @TestClassOrder(ClassOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @Execution(SAME_THREAD) -@ConfigFromFileBeforeAllTests(value = "ext/MyPropFile.properties") +@ConfigFromFileBeforeAllTests("ext/MyPropFile.properties") class ConfigFromFileMixedUsage1Test extends InterceptorTestBase { private static Object coreFoundInTest1; @@ -45,7 +45,7 @@ public void test2() { @Order(3) @Test - @ConfigFromFileBeforeThisTest(value = "ext/MyPropFile2.properties") + @ConfigFromFileBeforeThisTest("ext/MyPropFile2.properties") public void test3() throws NoSuchMethodException { assertFalse(AndHow.isInitialized()); @@ -158,7 +158,7 @@ public void test1() { */ @Nested @Order(4) - @ConfigFromFileBeforeEachTest(value = "ext/MyPropFileNest2.properties") + @ConfigFromFileBeforeEachTest("ext/MyPropFileNest2.properties") class NestAD { @Test diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java index 030de6e7..77bdb9f7 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ConfigFromFileMixedUsage2Test.java @@ -11,7 +11,7 @@ @TestClassOrder(ClassOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -@ConfigFromFileBeforeAllTests(value = "Conf1And2AsBob.properties", includeClasses = {Conf1.class, Conf2.class}) +@ConfigFromFileBeforeAllTests(value = "Conf1And2AsBob.properties", includeClasses = {Conf1.class, Conf2.class, Conf3.class}) class ConfigFromFileMixedUsage2Test { @Order(1) From 21be004c45ebaef2a0ad5d89c3e811892a6b7325 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Wed, 9 Nov 2022 19:00:52 -0600 Subject: [PATCH 15/22] Added tests --- .../andhow/junit5/ext/ExtensionBase.java | 5 ++++ .../andhow/junit5/ext/ExtensionBaseTest.java | 29 ++++++++++++------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java index 336a07b3..4c44e3af 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java @@ -23,6 +23,11 @@ protected ExtensionContext.Store getPerTestClassStore(ExtensionContext context) return context.getStore(getPerTestNamespace(context)); } + /** + * This implementation is currently wrong - should be based on test instance. + * @param context + * @return + */ protected ExtensionContext.Namespace getPerTestNamespace(ExtensionContext context) { return ExtensionContext.Namespace.create(getClass(), context.getRequiredTestClass()); } diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java index 65ba2caa..c8b8583f 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java @@ -17,9 +17,20 @@ class ExtensionBaseTest { //The context object that is passed to the test extension ExtensionContext extensionContext; + ExtensionContext.Namespace testInstanceNamespace; + + ExtensionContext.Namespace testMethodNamespace; + @BeforeEach public void setUp() throws NoSuchMethodException { + testInstanceNamespace = ExtensionContext.Namespace.create( + SimpleExtensionBase.class, this.getClass() + ); + + testMethodNamespace = ExtensionContext.Namespace.create( + SimpleExtensionBase.class, this, this.getClass().getMethod("setUp", null) + ); // // Setup mockito for the test @@ -37,6 +48,9 @@ public void setUp() throws NoSuchMethodException { @Test void getPerTestClassStore() { + ExtensionContext.Store myStore = extBase.getPerTestClassStore(extensionContext); + assertNotNull(myStore); + Mockito.verify(extensionContext).getStore(ArgumentMatchers.eq(testInstanceNamespace)); } /** @@ -47,26 +61,21 @@ void getPerTestClassStore() { void getPerTestNamespace() { ExtensionContext.Namespace sut = extBase.getPerTestNamespace(extensionContext); - ExtensionContext.Namespace ref = ExtensionContext.Namespace.create( - SimpleExtensionBase.class, this.getClass() - ); - - assertEquals(ref, sut); + assertEquals(testInstanceNamespace, sut); } @Test void getPerTestMethodStore() { + ExtensionContext.Store myStore = extBase.getPerTestMethodStore(extensionContext); + assertNotNull(myStore); + Mockito.verify(extensionContext).getStore(ArgumentMatchers.eq(testMethodNamespace)); } @Test void getPerTestMethodNamespace() throws NoSuchMethodException { ExtensionContext.Namespace sut = extBase.getPerTestMethodNamespace(extensionContext); - ExtensionContext.Namespace ref = ExtensionContext.Namespace.create( - SimpleExtensionBase.class, this, this.getClass().getMethod("setUp", null) - ); - - assertEquals(ref, sut); + assertEquals(testMethodNamespace, sut); } // Simple implementation to create a real subclass of ExtensionBase. From 653c0c01caeb27858f6cd84426d53184f69d8ef5 Mon Sep 17 00:00:00 2001 From: Eric Everman Date: Thu, 10 Nov 2022 11:25:53 -0600 Subject: [PATCH 16/22] generic getStore fully implemented ConfigFromFile simplified now that getStore is generic More unit testing --- .../ext/EnableJndiForThisTestClassExt.java | 9 +- .../ext/EnableJndiForThisTestMethodExt.java | 9 +- .../andhow/junit5/ext/ExtensionBase.java | 33 +++++++ .../andhow/junit5/ext/ExtensionType.java | 86 +++++++++++++++++++ .../ext/RestoreSysPropsAfterEachTestExt.java | 5 ++ .../ext/RestoreSysPropsAfterThisTestExt.java | 9 +- .../EnableJndiForThisTestClassExtTest.java | 18 ++++ .../EnableJndiForThisTestMethodExtTest.java | 18 ++++ .../andhow/junit5/ext/ExtensionBaseTest.java | 5 +- .../RestoreSysPropsAfterEachTestExtTest.java | 18 ++++ .../RestoreSysPropsAfterThisTestExtTest.java | 18 ++++ .../junit5/ext/ConfigFromFileBaseExt.java | 70 ++++----------- .../ext/ConfigFromFileBeforeAllTestsExt.java | 9 +- .../ext/ConfigFromFileBeforeEachTestExt.java | 9 +- .../ext/ConfigFromFileBeforeThisTestExt.java | 9 +- .../ext/KillAndHowBeforeAllTestsExt.java | 5 ++ .../ext/KillAndHowBeforeEachTestExt.java | 7 ++ .../ext/KillAndHowBeforeThisTestExt.java | 9 +- ...nfigFromFileBaseExtDefaultPackageTest.java | 6 ++ ...nfigFromFileBeforeAllTestsExtUnitTest.java | 18 ++++ ...nfigFromFileBeforeEachTestExtUnitTest.java | 18 ++++ ...nfigFromFileBeforeThisTestExtUnitTest.java | 18 ++++ .../junit5/ext/ConfigFromFileExtUnitTest.java | 74 ++++++++++------ ... KillAndHowBeforeAllTestsExtUnitTest.java} | 12 ++- ...ndHowBeforeEachTestExtensionUnitTest.java} | 14 ++- ...ndHowBeforeThisTestExtensionUnitTest.java} | 12 ++- 26 files changed, 424 insertions(+), 94 deletions(-) create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionType.java create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExtTest.java create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExtTest.java create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExtTest.java create mode 100644 junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExtTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeAllTestsExtUnitTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeEachTestExtUnitTest.java create mode 100644 junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBeforeThisTestExtUnitTest.java rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/{KillAndHowBeforeAllTestsExtTest.java => KillAndHowBeforeAllTestsExtUnitTest.java} (90%) rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/{KillAndHowBeforeEachTestExtensionTest.java => KillAndHowBeforeEachTestExtensionUnitTest.java} (88%) rename junit5-extensions/junit5-extensions-with-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/{KillAndHowBeforeThisTestExtensionTest.java => KillAndHowBeforeThisTestExtensionUnitTest.java} (91%) diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExt.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExt.java index 6f0db62e..16b0c277 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExt.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExt.java @@ -17,6 +17,11 @@ public class EnableJndiForThisTestClassExt extends ExtensionBase protected final static String KEY = "KEY"; + @Override + public ExtensionType getExtensionType() { + return ExtensionType.MODIFY_ENV_ALL_TESTS; + } + /** * Enable JNDI for use in an individual test method. * @@ -33,7 +38,7 @@ public void beforeAll(ExtensionContext context) throws Exception { // // Store sys props as they were before this method - getPerTestClassStore(context).put(KEY, System.getProperties().clone()); + getStore(context).put(KEY, System.getProperties().clone()); System.setProperty("java.naming.factory.initial", "org.osjava.sj.SimpleJndiContextFactory"); System.setProperty("org.osjava.sj.delimiter", "/"); @@ -60,7 +65,7 @@ public void afterAll(ExtensionContext context) throws Exception { // // Restore Sys Props - Properties p = getPerTestClassStore(context).remove(KEY, Properties.class); + Properties p = getStore(context).remove(KEY, Properties.class); System.setProperties(p); } diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExt.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExt.java index 566abaeb..43ef5336 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExt.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExt.java @@ -17,6 +17,11 @@ public class EnableJndiForThisTestMethodExt extends ExtensionBase protected final static String KEY = "KEY"; + @Override + public ExtensionType getExtensionType() { + return ExtensionType.MODIFY_ENV_THIS_TEST; + } + /** * Enable JNDI for use in an individual test method. * @@ -35,7 +40,7 @@ public void beforeEach(ExtensionContext context) throws Exception { // // Store sys props as they were before this method - getPerTestMethodStore(context).put(KEY, System.getProperties().clone()); + getStore(context).put(KEY, System.getProperties().clone()); System.setProperty("java.naming.factory.initial", "org.osjava.sj.SimpleJndiContextFactory"); System.setProperty("org.osjava.sj.delimiter", "/"); @@ -63,7 +68,7 @@ public void afterEach(ExtensionContext context) throws Exception { // // Restore Sys Props - Properties p = getPerTestMethodStore(context).remove(KEY, Properties.class); + Properties p = getStore(context).remove(KEY, Properties.class); System.setProperties(p); } diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java index 4c44e3af..04d98c3d 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionBase.java @@ -1,9 +1,42 @@ package org.yarnandtail.andhow.junit5.ext; import org.junit.jupiter.api.extension.*; +import static org.yarnandtail.andhow.junit5.ext.ExtensionType.Storage.*; public abstract class ExtensionBase { + /** + * The ExtensionType that describes the basic behaviors and attributes of the extension. + * + * @return + */ + protected abstract ExtensionType getExtensionType(); + + + /** + * Get the appropriate Store based on the getExtensionType() value of the Extension. + * + * @param context + * @return + * @throws IllegalStateException If the subclass's getExtensionType() returns a value that has + * a storage type of Storage.MIXTURE. Those implementations must determine the appropriate + * storage themselves. + */ + protected ExtensionContext.Store getStore(ExtensionContext context) { + ExtensionType type = getExtensionType(); + + switch (type.getStorage()) { + case TEST_INSTANCE: + return getPerTestClassStore(context); + case TEST_METHOD: + return getPerTestMethodStore(context); + default: + throw new IllegalStateException("Cannot call getStore() if the getExtensionType() returns " + + "a type that doesn't use TEST_INSTANCE or TEST_METHOD storage."); + } + + } + /** * Create or return a unique storage space, which is unique per the test class. * diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionType.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionType.java new file mode 100644 index 00000000..f952cfb5 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ExtensionType.java @@ -0,0 +1,86 @@ +package org.yarnandtail.andhow.junit5.ext; + +/** + * Rich enum to describe a JUnit extension, including key properties for how the extension + * interacts with runtime storage and how extensions interact with each other. + */ +public enum ExtensionType { + CONFIG_EACH_TEST(Storage.TEST_METHOD, Scope.EACH_TEST, Effect.CONFIGURE), + CONFIG_ALL_TESTS(Storage.TEST_INSTANCE, Scope.TEST_CLASS, Effect.CONFIGURE), + CONFIG_THIS_TEST(Storage.TEST_METHOD, Scope.SINGLE_TEST, Effect.CONFIGURE), + KILL_EACH_TEST(Storage.TEST_METHOD, Scope.EACH_TEST, Effect.KILL), + KILL_ALL_TESTS(Storage.TEST_INSTANCE, Scope.TEST_CLASS, Effect.KILL), + KILL_THIS_TEST(Storage.TEST_METHOD, Scope.SINGLE_TEST, Effect.KILL), + MODIFY_ENV_EACH_TEST(Storage.TEST_METHOD, Scope.EACH_TEST, Effect.ENVIRONMENT), + MODIFY_ENV_ALL_TESTS(Storage.TEST_INSTANCE, Scope.TEST_CLASS, Effect.ENVIRONMENT), + MODIFY_ENV_THIS_TEST(Storage.TEST_METHOD, Scope.SINGLE_TEST, Effect.ENVIRONMENT), + OTHER_EACH_TEST(Storage.TEST_METHOD, Scope.EACH_TEST, Effect.OTHER), + OTHER_ALL_TESTS(Storage.TEST_INSTANCE, Scope.TEST_CLASS, Effect.OTHER), + OTHER_THIS_TEST(Storage.TEST_METHOD, Scope.SINGLE_TEST, Effect.OTHER), + OTHER(Storage.MIXTURE, Scope.MIXTURE, Effect.OTHER), + ; + + private Storage _storage; + private Scope _scope; + private Effect _effect; + + private ExtensionType(Storage storage, Scope scope, Effect effect) { + _storage = storage; + _scope = scope; + _effect = effect; + } + + public Storage getStorage() { + return _storage; + } + + public Scope getScope() { + return _scope; + } + + public Effect getEffect() { + return _effect; + } + + static enum Storage { + /** State should be stored in the context of the test instance */ + TEST_INSTANCE, + /** State should be stored in the context of the test method */ + TEST_METHOD, + /** Custom storage / mixed usage - No one correct answer */ + MIXTURE + } + + /** + * The level at which this extension (and any associated annotation) operate at. + *

    + * Some extensions operate at the test class level, where they use beforeAll/afterAll + * events, others at the test method level w/ beforeEach/afterEach events. + */ + static enum Scope { + /** Uses BeforeAll and AfterAll class-level events */ + TEST_CLASS, + /** Uses BeforeEach and AfterEach events applied to all tests in the class, + * (annotated at the class level) */ + EACH_TEST, + /** Uses BeforeEach and AfterEach events applied to a single test method, + * (annotated at the method level) */ + SINGLE_TEST, + /** Uses a mixture of BeforeAll BeforeEach, etc.. Annotations may be on the class or method */ + MIXTURE + } + + /** + * The effect of the extension, within the AndHow world. + */ + static enum Effect { + /** Configures AndHow */ + CONFIGURE, + /** Kills the AndHow configured state */ + KILL, + /** Affects the environment that AndHow could use for configuration */ + ENVIRONMENT, + /** Other effect that does not interact with AndHow */ + OTHER + } +} diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExt.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExt.java index af8084ab..1d9ccd8a 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExt.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExt.java @@ -15,6 +15,11 @@ public class RestoreSysPropsAfterEachTestExt extends ExtensionBase protected final static String KEY = "KEY"; + @Override + public ExtensionType getExtensionType() { + return ExtensionType.OTHER; + } + /** * Store the original Sys Props prior to any testing. * diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExt.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExt.java index 8075b5b0..7a76dedf 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExt.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExt.java @@ -14,6 +14,11 @@ public class RestoreSysPropsAfterThisTestExt extends ExtensionBase protected final static String KEY = "KEY"; + @Override + public ExtensionType getExtensionType() { + return ExtensionType.OTHER_THIS_TEST; + } + /** * Store the Sys Props as they were prior to this test. * @@ -25,7 +30,7 @@ public class RestoreSysPropsAfterThisTestExt extends ExtensionBase */ @Override public void beforeEach(ExtensionContext context) throws Exception { - getPerTestMethodStore(context).put(KEY, System.getProperties().clone()); + getStore(context).put(KEY, System.getProperties().clone()); } /** @@ -39,7 +44,7 @@ public void beforeEach(ExtensionContext context) throws Exception { */ @Override public void afterEach(ExtensionContext context) throws Exception { - Properties p = getPerTestMethodStore(context).remove(KEY, Properties.class); + Properties p = getStore(context).remove(KEY, Properties.class); System.setProperties(p); } diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExtTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExtTest.java new file mode 100644 index 00000000..6f79db77 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestClassExtTest.java @@ -0,0 +1,18 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class EnableJndiForThisTestClassExtTest { + + @Test + void getExtensionType() { + EnableJndiForThisTestClassExt ext = new EnableJndiForThisTestClassExt(); + ExtensionType type = ext.getExtensionType(); + + assertEquals(ExtensionType.Storage.TEST_INSTANCE, type.getStorage()); + assertEquals(ExtensionType.Effect.ENVIRONMENT, type.getEffect()); + assertEquals(ExtensionType.Scope.TEST_CLASS, type.getScope()); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExtTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExtTest.java new file mode 100644 index 00000000..fc6f60d9 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/EnableJndiForThisTestMethodExtTest.java @@ -0,0 +1,18 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class EnableJndiForThisTestMethodExtTest { + + @Test + void getExtensionType() { + EnableJndiForThisTestMethodExt ext = new EnableJndiForThisTestMethodExt(); + ExtensionType type = ext.getExtensionType(); + + assertEquals(ExtensionType.Storage.TEST_METHOD, type.getStorage()); + assertEquals(ExtensionType.Effect.ENVIRONMENT, type.getEffect()); + assertEquals(ExtensionType.Scope.SINGLE_TEST, type.getScope()); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java index c8b8583f..9e3df643 100644 --- a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/ExtensionBaseTest.java @@ -80,6 +80,9 @@ void getPerTestMethodNamespace() throws NoSuchMethodException { // Simple implementation to create a real subclass of ExtensionBase. static class SimpleExtensionBase extends ExtensionBase { - + @Override + public ExtensionType getExtensionType() { + return ExtensionType.OTHER; + } } } \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExtTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExtTest.java new file mode 100644 index 00000000..aa118cb3 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterEachTestExtTest.java @@ -0,0 +1,18 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RestoreSysPropsAfterEachTestExtTest { + + @Test + void getExtensionType() { + RestoreSysPropsAfterEachTestExt ext = new RestoreSysPropsAfterEachTestExt(); + ExtensionType type = ext.getExtensionType(); + + assertEquals(ExtensionType.Storage.MIXTURE, type.getStorage()); + assertEquals(ExtensionType.Effect.OTHER, type.getEffect()); + assertEquals(ExtensionType.Scope.MIXTURE, type.getScope()); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExtTest.java b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExtTest.java new file mode 100644 index 00000000..5e3b1d03 --- /dev/null +++ b/junit5-extensions/junit5-extensions-no-andhow-dependency/src/test/java/org/yarnandtail/andhow/junit5/ext/RestoreSysPropsAfterThisTestExtTest.java @@ -0,0 +1,18 @@ +package org.yarnandtail.andhow.junit5.ext; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RestoreSysPropsAfterThisTestExtTest { + + @Test + void getExtensionType() { + RestoreSysPropsAfterThisTestExt ext = new RestoreSysPropsAfterThisTestExt(); + ExtensionType type = ext.getExtensionType(); + + assertEquals(ExtensionType.Storage.TEST_METHOD, type.getStorage()); + assertEquals(ExtensionType.Effect.OTHER, type.getEffect()); + assertEquals(ExtensionType.Scope.SINGLE_TEST, type.getScope()); + } +} \ No newline at end of file diff --git a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java index 8c1d9ef8..995dbd57 100644 --- a/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java +++ b/junit5-extensions/junit5-extensions-with-andhow-dependency/src/main/java/org/yarnandtail/andhow/junit5/ext/ConfigFromFileBaseExt.java @@ -111,12 +111,20 @@ public ConfigFromFileBaseExt() { } */ protected abstract Class[] getClassesInScopeFromAnnotation(ExtensionContext context); + + // + //The beforeAll/beforeEach/afterAllAfter each can probably be consolidated into + //generic before and after at this point, since the storage choice is abstracted. + + /** - * Configure AndHow for a unit test class. + * Configure AndHow for a unit test class or test method. *

    - * This does the following: + * The differences between class-level and method level is state storage. + * That is determined by the ExtensionType returned by getExtensionType(). + * This method does the following: *