Skip to content

Commit

Permalink
CamelTestSupport style of testing apache#3511
Browse files Browse the repository at this point in the history
  • Loading branch information
JiriOndrusek committed Jun 23, 2022
1 parent 303c2ce commit 8f6d54d
Show file tree
Hide file tree
Showing 53 changed files with 3,014 additions and 168 deletions.
13 changes: 0 additions & 13 deletions docs/modules/ROOT/examples/components/mybatis-bean.yml

This file was deleted.

13 changes: 0 additions & 13 deletions docs/modules/ROOT/examples/components/mybatis.yml

This file was deleted.

53 changes: 0 additions & 53 deletions docs/modules/ROOT/pages/reference/extensions/mybatis.adoc

This file was deleted.

17 changes: 17 additions & 0 deletions docs/modules/ROOT/pages/user-guide/testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,20 @@ class MyTest {
----

More examples of WireMock usage can be found in the Camel Quarkus integration test source tree such as https://github.com/apache/camel-quarkus/tree/main/integration-tests/geocoder[Geocoder].

== Testing with `CamelTestSupport` in JVM mode

Sometimes you want to use test constructs from Camel (like `MockEndpoint`, `AdviceWith` , ...) in *JVM* mode.

`CamelTestSupport` is not designed to work in Quarkus, you can use `CamelQuarkusTestSupport` instead. Test has to be annotated by `@QuarkusTest` and extend `CamelQuarkusTest` to allow you to use Camel test constructs. Support is based on component `camel-test-junit5`, see https://camel.apache.org/components/3.17.x/others/test-junit5.html[documentation] for more information.

There are several limitations:

* Quarkus has to run tests in a custom classloader which JUnit is not aware of (see the https://quarkus.io/guides/getting-started-testing#applying-interceptors-to-tests[documentation]). Therefore, if Jupiter's callback (i.e. `org.junit.jupiter.api.extension.BeforeEachCallback`) is used, it won't work. Use the quarkus callbacks instead (see the https://quarkus.io/guides/getting-started-testing#enrichment-via-quarkustestcallback[documentation]) or use annotations like `@org.junit.jupiter.api.BeforeEach`.
* CamelQuarkus lifecycle does not allow to start/stop Camel context. Context is started before execution of the first test and closed after the finish of the last one. Test has to be written with consideration with this limitation. If it is not possible to write a test with such limitation, `@TestProfile` could be used. Test profile forces quarkus to restart its engine, therefore it creates a new Camel context (see the https://quarkus.io/guides/getting-started-testing#testing_different_profiles/[documentation] about this feature).
* Camel Quarkus executes the production of beans during the build phase. Because all of the tests are build together, exclusion behavior is implemented into `CamelQuarkusTestSupport`. If a producer of the specific type and name is used in one tests, the instance will be the same for the rest of the tests.

todo Test constructs which are supported:

* AdviceWith

4 changes: 4 additions & 0 deletions extensions-core/core/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus.arc</groupId>
<artifactId>arc-processor</artifactId>
</dependency>

<!-- camel -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class InjectionPointsProcessor {
.createSimple(EndpointInject.class.getName());
private static final DotName PRODUCE_ANNOTATION = DotName
.createSimple(Produce.class.getName());
private static final DotName TEST_SUPPORT_CLASS_NAME = DotName
.createSimple("org.apache.camel.quarkus.test.CamelQuarkusTestSupport");

private static SyntheticBeanBuildItem syntheticBean(DotName name, Supplier<?> creator) {
return SyntheticBeanBuildItem.configure(name)
Expand Down Expand Up @@ -226,12 +228,16 @@ void syntheticBeans(
BuildProducer<SyntheticBeanBuildItem> syntheticBeans,
BuildProducer<NativeImageProxyDefinitionBuildItem> proxyDefinitions) {

Set<String> alreadyCreated = new HashSet<>();

for (AnnotationInstance annot : index.getIndex().getAnnotations(ENDPOINT_INJECT_ANNOTATION)) {
final AnnotationTarget target = annot.target();
switch (target.kind()) {
case FIELD: {
final FieldInfo field = target.asField();
endpointInjectBeans(recorder, syntheticBeans, index.getIndex(), annot, field.type().name());
if (!excludeTestSyntheticBeanDuplicities(annot, alreadyCreated, field.declaringClass(), index.getIndex())) {
endpointInjectBeans(recorder, syntheticBeans, index.getIndex(), annot, field.type().name());
}
break;
}
case METHOD: {
Expand All @@ -251,8 +257,10 @@ void syntheticBeans(
switch (target.kind()) {
case FIELD: {
final FieldInfo field = target.asField();
produceBeans(recorder, capabilities, syntheticBeans, proxyDefinitions, beanCapabilityAvailable,
index.getIndex(), annot, field.type().name(), field.name(), field.declaringClass().name());
if (!excludeTestSyntheticBeanDuplicities(annot, alreadyCreated, field.declaringClass(), index.getIndex())) {
produceBeans(recorder, capabilities, syntheticBeans, proxyDefinitions, beanCapabilityAvailable,
index.getIndex(), annot, field.type().name(), field.name(), field.declaringClass().name());
}
break;
}
case METHOD: {
Expand All @@ -266,6 +274,47 @@ void syntheticBeans(
}
}

private boolean excludeTestSyntheticBeanDuplicities(AnnotationInstance annot, Set<String> alreadyCreated,
ClassInfo declaringClass, IndexView index) {
String identifier = annot.toString(false) + ":" + getTargetClass(annot).toString();

if (extendsCamelQuarkusTest(declaringClass, index)) {
if (alreadyCreated.contains(identifier)) {
// System.out.println(">>>>>>>>>> already exists: " + identifier);
return true;
} else {
// System.out.println(">>>>>>>>>> creating: " + identifier);
alreadyCreated.add(identifier);
}
}
return false;
}

private DotName getTargetClass(AnnotationInstance annot) {
switch (annot.target().kind()) {
case FIELD:
return annot.target().asField().type().name();
case METHOD:
return annot.target().asMethod().returnType().name();
default:
return null;
}
}

private boolean extendsCamelQuarkusTest(ClassInfo declaringClass, IndexView indexView) {
if (declaringClass == null) {
return false;
}

if (TEST_SUPPORT_CLASS_NAME.equals(declaringClass.name())) {
return true;
}

//iterate over parent until found CamelQuarkusTest or null
return (declaringClass.superName() != null &&
extendsCamelQuarkusTest(indexView.getClassByName(declaringClass.superName()), indexView));
}

void produceBeans(CamelRecorder recorder, List<CapabilityBuildItem> capabilities,
BuildProducer<SyntheticBeanBuildItem> syntheticBeans,
BuildProducer<NativeImageProxyDefinitionBuildItem> proxyDefinitions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"supportLevel": "Preview",
"groupId": "org.apache.camel.quarkus",
"artifactId": "camel-quarkus-qute-component",
"version": "2.10.0-SNAPSHOT",
"version": "2.11.0-SNAPSHOT",
"scheme": "qute",
"extendsScheme": "",
"syntax": "qute:resourceUri",
Expand All @@ -33,6 +33,6 @@
"allowTemplateFromHeader": { "kind": "parameter", "displayName": "Allow Template From Header", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether to allow to use resource template from header or not (default false). Enabling this allows to specify dynamic templates via message header. However this can be seen as a potential security vulnerability if the header is coming from a malicious user, so use this with care." },
"contentCache": { "kind": "parameter", "displayName": "Content Cache", "group": "producer", "label": "", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Sets whether to use resource content cache or not" },
"encoding": { "kind": "parameter", "displayName": "Encoding", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "Character encoding of the resource content." },
"lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }
"lazyStartProducer": { "kind": "parameter", "displayName": "Lazy Start Producer", "group": "producer (advanced)", "label": "producer,advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel's routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing." }
}
}
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@
<module>integration-test-groups</module>
<module>docs</module>
<module>integration-tests-jvm</module>
<module>test-framework</module>
</modules>

<developers>
Expand Down Expand Up @@ -586,6 +587,10 @@
<path>integration-tests-support</path>
<artifactIdPrefix>camel-quarkus-integration-test-support-</artifactIdPrefix>
</extensionDir>
<extensionDir>
<path>test-framework</path>
<artifactIdPrefix>camel-quarkus-test-framework</artifactIdPrefix>
</extensionDir>
</extensionDirs>
</configuration>
</plugin>
Expand Down
Loading

0 comments on commit 8f6d54d

Please sign in to comment.