generated from micronaut-projects/micronaut-project-template
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement test resources property provider factory (#231)
* Implement test resources property provider factory This commit introduces a new annotation, `@TestResourceProperties`, which can be applied on a test in order to tell that the test needs to resolve one or more test resource properties _before_ the application context is available. For example, with: ```java @MicronautTest @TestResourcesProperties( value = "redis.url" ) class SomeTest { ... } ``` Then the value of the `redis.url` property will automatically be fetched from the test resources service and made available as if it had been done in a `TestPropertyProvider`. The difference is that this without this annotation, it was required to call the test resources client directly in the provider, which was error prone. In particular, the `properties` argument of the `resolve` call are not easy to figure out. This annotation is therefore a convention to avoid having to call the client directly. However, in some cases it might be necessary to read a property in order to compute a different one which needs to be available in the test. This can be done by adding a `providers` argument to the annotation: ```java @TestResourcesProperties( value = "rabbitmq.uri", providers = RabbitTest.RabbitMQProvider.class ) class RabbitTest { // ... @ReflectiveAccess public static class RabbitMQProvider implements TestResourcesPropertyProvider { @OverRide public Map<String, String> provide(Map<String, Object> testProperties) { String uri = (String) testProperties.get("rabbitmq.uri"); return Map.of( "rabbitmq.servers.product-cluster.port", String.valueOf(URI.create(uri).getPort()) ); } } } ``` The `TestResourcesPropertyProvider` type has access to all properties which are already resolved at the moment it is called, which includes the properties asked in `@TestResourcesProperties`. This should make the implementation of micronaut-projects/micronaut-nats#321 easier. * Remove accidentally added files * Add javadocs * Add missing test * Make spotless happy * Add nullability annotations * Fix checkstyle and japicmp
- Loading branch information
Showing
19 changed files
with
674 additions
and
1 deletion.
There are no files selected for viewing
12 changes: 12 additions & 0 deletions
12
buildSrc/src/main/groovy/io.micronaut.build.internal.test-resources-mntest-extension.gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
plugins { | ||
id 'io.micronaut.build.internal.test-resources-simple-module' | ||
id 'io.micronaut.build.internal.test-fixtures' | ||
} | ||
|
||
components.java.withVariantsFromConfiguration(configurations.testFixturesApiElements) { skip() } | ||
components.java.withVariantsFromConfiguration(configurations.testFixturesRuntimeElements) { skip() } | ||
|
||
micronautBuild { | ||
def (String major, String minor, String patch) = (version - '-SNAPSHOT').split("[.]") | ||
binaryCompatibility.enabled = major.toInteger() >=2 && minor.toInteger() >= 0 && patch.toInteger() > 0 | ||
} |
9 changes: 9 additions & 0 deletions
9
buildSrc/src/main/groovy/io.micronaut.build.internal.test-resources-simple-module.gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* Use this plugin for modules which are not Micronaut modules | ||
* but follow the same conventions (e.g need publishing, binary | ||
* compatibilty, etc...) | ||
*/ | ||
plugins { | ||
id 'io.micronaut.build.internal.test-resources-base' | ||
id 'io.micronaut.build.internal.base-module' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
test-resources-extensions/test-resources-extensions-core/build.gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
plugins { | ||
id 'io.micronaut.build.internal.test-resources-mntest-extension' | ||
} | ||
|
||
description = """ | ||
Provides Micronaut Test extensions which make it easier to integrate | ||
with Test Resources. | ||
""" | ||
|
||
dependencies { | ||
// Sync'es the version of JUnit 5 used by Micronaut Test | ||
api(platform(mnTest.micronaut.test.bom)) | ||
api(mnTest.micronaut.test.core) | ||
implementation(projects.micronautTestResourcesClient) | ||
|
||
testAnnotationProcessor(mn.micronaut.inject.java) | ||
testImplementation(mnTest.micronaut.test.junit5) | ||
testFixturesAnnotationProcessor(mn.micronaut.inject.java) | ||
testFixturesApi(libs.junit.platform.launcher) | ||
testFixturesApi(platform(mnTest.micronaut.test.bom)) | ||
testFixturesImplementation(projects.micronautTestResourcesClient) | ||
testRuntimeOnly(libs.junit.jupiter.engine) | ||
testRuntimeOnly(mn.micronaut.context) | ||
testRuntimeOnly(mn.snakeyaml) | ||
} |
96 changes: 96 additions & 0 deletions
96
...e/src/main/java/io/micronaut/test/extensions/testresources/TestResourcesClientHolder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
* Copyright 2017-2021 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micronaut.test.extensions.testresources; | ||
|
||
import io.micronaut.core.annotation.Internal; | ||
import io.micronaut.testresources.client.TestResourcesClient; | ||
|
||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* An internal class which can be used to inject a fake | ||
* test resources client, for testing purposes. | ||
*/ | ||
@Internal | ||
public final class TestResourcesClientHolder { | ||
private static TestResourcesClient CLIENT; | ||
|
||
private TestResourcesClientHolder() { | ||
|
||
} | ||
|
||
public static void set(TestResourcesClient client) { | ||
CLIENT = client; | ||
} | ||
|
||
public static TestResourcesClient get() { | ||
return CLIENT; | ||
} | ||
|
||
public static TestResourcesClient lazy() { | ||
return new LazyTestResourcesClient(); | ||
} | ||
|
||
private static class LazyTestResourcesClient implements TestResourcesClient { | ||
|
||
private static <T> T nullSafe(Supplier<T> value) { | ||
if (CLIENT == null) { | ||
return null; | ||
} | ||
return value.get(); | ||
} | ||
|
||
@Override | ||
public List<String> getResolvableProperties(Map<String, Collection<String>> propertyEntries, Map<String, Object> testResourcesConfig) { | ||
return nullSafe(CLIENT::getResolvableProperties); | ||
} | ||
|
||
@Override | ||
public Optional<String> resolve(String name, Map<String, Object> properties, Map<String, Object> testResourcesConfiguration) { | ||
return nullSafe(() -> CLIENT.resolve(name, properties, testResourcesConfiguration)); | ||
} | ||
|
||
@Override | ||
public List<String> getRequiredProperties(String expression) { | ||
return nullSafe(() -> CLIENT.getRequiredProperties(expression)); | ||
} | ||
|
||
@Override | ||
public List<String> getRequiredPropertyEntries() { | ||
return nullSafe(CLIENT::getRequiredPropertyEntries); | ||
} | ||
|
||
@Override | ||
public boolean closeAll() { | ||
return nullSafe(CLIENT::closeAll); | ||
} | ||
|
||
@Override | ||
public boolean closeScope(String id) { | ||
return nullSafe(() -> CLIENT.closeScope(id)); | ||
} | ||
|
||
@Override | ||
public List<String> getResolvableProperties() { | ||
return nullSafe(CLIENT::getResolvableProperties); | ||
} | ||
|
||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
.../main/java/io/micronaut/test/extensions/testresources/TestResourcesPropertiesFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
/* | ||
* Copyright 2017-2021 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micronaut.test.extensions.testresources; | ||
|
||
import io.micronaut.test.extensions.testresources.annotation.TestResourcesProperties; | ||
import io.micronaut.test.support.TestPropertyProvider; | ||
import io.micronaut.test.support.TestPropertyProviderFactory; | ||
import io.micronaut.testresources.client.TestResourcesClientFactory; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
public class TestResourcesPropertiesFactory implements TestPropertyProviderFactory { | ||
@Override | ||
public TestPropertyProvider create(Map<String, Object> properties, Class<?> testClass) { | ||
return new TestResourcesTestPropertyProvider(testClass, properties); | ||
|
||
} | ||
|
||
private static TestResourcesPropertyProvider instantitateProvider(Class<? extends TestResourcesPropertyProvider> provider) { | ||
try { | ||
return provider.getDeclaredConstructor().newInstance(); | ||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | | ||
NoSuchMethodException e) { | ||
throw new RuntimeException("Test resources property provider must have a public constructor without arguments", e); | ||
} | ||
} | ||
|
||
private static class TestResourcesTestPropertyProvider implements TestPropertyProvider { | ||
public static final String TEST_RESOURCES_PROPERTY_PREFIX = "test-resources."; | ||
private final Class<?> testClass; | ||
private final Map<String, Object> properties; | ||
|
||
public TestResourcesTestPropertyProvider(Class<?> testClass, Map<String, Object> properties) { | ||
this.testClass = testClass; | ||
this.properties = properties; | ||
} | ||
|
||
@Override | ||
public Map<String, String> getProperties() { | ||
TestResourcesProperties annotation = testClass.getAnnotation(TestResourcesProperties.class); | ||
if (annotation != null) { | ||
String[] requestedProperties = annotation.value(); | ||
var client = TestResourcesClientFactory.fromSystemProperties() | ||
.orElse(TestResourcesClientHolder.lazy()); | ||
var testResourcesConfig = properties.entrySet() | ||
.stream() | ||
.filter(e -> e.getKey().startsWith(TEST_RESOURCES_PROPERTY_PREFIX)) | ||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | ||
Map<String, String> resolvedProperties = Stream.of(requestedProperties) | ||
.map(v -> new Object() { | ||
private final String key = v; | ||
private final String value = client.resolve(v, Map.of(), testResourcesConfig).orElse(null); | ||
}) | ||
.filter(o -> o.value != null) | ||
.collect(Collectors.toMap(e -> e.key, e -> e.value)); | ||
|
||
// Result represents what properties we're going to expose to tests | ||
Map<String, String> result = new HashMap<>(resolvedProperties); | ||
// Context represents what is available to resolvers for them to | ||
// compute results | ||
Map<String, Object> context = new HashMap<>(properties); | ||
context.putAll(resolvedProperties); | ||
result.putAll(resolvedProperties); | ||
Class<? extends TestResourcesPropertyProvider>[] providers = annotation.providers(); | ||
for (Class<? extends TestResourcesPropertyProvider> provider : providers) { | ||
var testResourcesPropertyProvider = instantitateProvider(provider); | ||
Map<String, String> map = testResourcesPropertyProvider.provide(Collections.unmodifiableMap(context)); | ||
context.putAll(map); | ||
result.putAll(map); | ||
} | ||
return result; | ||
} | ||
return Map.of(); | ||
} | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
...c/main/java/io/micronaut/test/extensions/testresources/TestResourcesPropertyProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* | ||
* Copyright 2017-2021 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micronaut.test.extensions.testresources; | ||
|
||
import io.micronaut.core.annotation.NonNull; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* A test resources property provider is a type which | ||
* must be explicitly declared in via the {@link io.micronaut.test.extensions.testresources.annotation.TestResourcesProperties} | ||
* annotation. | ||
* <p/> | ||
* It is responsible for supplying additional test properties, | ||
* given the set of properties which are available before the | ||
* application context is started. | ||
* <p/> | ||
* It can be used, in particular, to derive new properties | ||
* from other properties resolved by the test resources client. | ||
* <p/> | ||
* This works in a very similar way as {@link io.micronaut.test.support.TestPropertyProvider}, | ||
* but has access to other properties in order to perform | ||
* computation based on the value of these properties. | ||
*/ | ||
@FunctionalInterface | ||
public interface TestResourcesPropertyProvider { | ||
/** | ||
* Returns a map of properties which need to be exposed | ||
* to the application context, given the map of properties | ||
* which are already available during setup. | ||
* | ||
* These properties typically include the properties | ||
* visible in the configuration files which do not require | ||
* access to test resources. | ||
* | ||
* @param testProperties the set of properties available | ||
* @return a map of properties to be added | ||
*/ | ||
@NonNull | ||
Map<String, String> provide(@NonNull Map<String, Object> testProperties); | ||
} |
Oops, something went wrong.