diff --git a/pom.xml b/pom.xml
index 2f1f7f488..e28507a69 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,8 @@
vaadin-testbench-unit
vaadin-testbench-unit-junit5
vaadin-testbench-unit-quarkus
+ testing-support/uiunit-test-components
+ testing-support/uiunit-component-testers
diff --git a/testing-support/uiunit-component-testers/pom.xml b/testing-support/uiunit-component-testers/pom.xml
new file mode 100644
index 000000000..201e59079
--- /dev/null
+++ b/testing-support/uiunit-component-testers/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-testbench-parent
+ 10.0-SNAPSHOT
+ ../../pom.xml
+
+
+ uiunit-component-testers
+ jar
+
+
+ true
+
+
+
+
+ com.vaadin
+ flow-server
+ ${flow.version}
+ provided
+
+
+ com.vaadin
+ vaadin-testbench-unit-shared
+ ${project.version}
+ provided
+
+
+ com.vaadin
+ uiunit-test-components
+ ${project.version}
+ provided
+ true
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+
+ true
+
+ 1.6.8
+
+
+
+
\ No newline at end of file
diff --git a/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java
new file mode 100644
index 000000000..c535d84eb
--- /dev/null
+++ b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponentTester.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2000-2025 Vaadin Ltd
+ *
+ * This program is available under Vaadin Commercial License and Service Terms.
+ *
+ * See for the full
+ * license.
+ */
+package com.vaadin.testbench.dontscan.noclassdeffound;
+
+import com.vaadin.testbench.unit.ComponentTester;
+import com.vaadin.testbench.unit.Tests;
+
+/**
+ * Dummy tester referencing a class from a third-party JAR that might not be
+ * available at runtime. This is used to verify that scanning for testers does
+ * not fail when the referenced component class is not on the classpath.
+ */
+@Tests(NoClassDefFoundComponent.class)
+public class NoClassDefFoundComponentTester
+ extends ComponentTester {
+
+ public NoClassDefFoundComponentTester(T component) {
+ super(component);
+ }
+}
diff --git a/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java
new file mode 100644
index 000000000..c5d8d7a94
--- /dev/null
+++ b/testing-support/uiunit-component-testers/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponentTester.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2000-2025 Vaadin Ltd
+ *
+ * This program is available under Vaadin Commercial License and Service Terms.
+ *
+ * See for the full
+ * license.
+ */
+package com.vaadin.testbench.dontscan.typenotpresent;
+
+import com.vaadin.flow.component.Component;
+import com.vaadin.testbench.unit.ComponentTester;
+import com.vaadin.testbench.unit.Tests;
+
+/**
+ * Dummy tester whose annotation references a class from a third-party JAR that
+ * might not be available at runtime. This is used to verify that scanning for
+ * testers does not fail when the referenced component class is not on the
+ * classpath.
+ */
+@Tests(TypeNotPresentComponent.class)
+public class TypeNotPresentComponentTester extends ComponentTester {
+
+ public TypeNotPresentComponentTester(Component component) {
+ super(component);
+ }
+}
diff --git a/testing-support/uiunit-test-components/pom.xml b/testing-support/uiunit-test-components/pom.xml
new file mode 100644
index 000000000..345b5aeba
--- /dev/null
+++ b/testing-support/uiunit-test-components/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-testbench-parent
+ 10.0-SNAPSHOT
+ ../../pom.xml
+
+
+ uiunit-test-components
+ jar
+
+
+ true
+
+
+
+
+ com.vaadin
+ flow-server
+ ${flow.version}
+ provided
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+
+ true
+
+ 1.6.8
+
+
+
+
\ No newline at end of file
diff --git a/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java
new file mode 100644
index 000000000..285827552
--- /dev/null
+++ b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/noclassdeffound/NoClassDefFoundComponent.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2000-2025 Vaadin Ltd
+ *
+ * This program is available under Vaadin Commercial License and Service Terms.
+ *
+ * See for the full
+ * license.
+ */
+
+package com.vaadin.testbench.dontscan.noclassdeffound;
+
+import com.vaadin.flow.component.Component;
+import com.vaadin.flow.component.Tag;
+
+@Tag("span")
+public class NoClassDefFoundComponent extends Component {
+}
diff --git a/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java
new file mode 100644
index 000000000..92fb41b24
--- /dev/null
+++ b/testing-support/uiunit-test-components/src/main/java/com/vaadin/testbench/dontscan/typenotpresent/TypeNotPresentComponent.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2000-2025 Vaadin Ltd
+ *
+ * This program is available under Vaadin Commercial License and Service Terms.
+ *
+ * See for the full
+ * license.
+ */
+
+package com.vaadin.testbench.dontscan.typenotpresent;
+
+import com.vaadin.flow.component.Component;
+import com.vaadin.flow.component.Tag;
+
+@Tag("span")
+public class TypeNotPresentComponent extends Component {
+}
diff --git a/vaadin-testbench-unit-junit5/pom.xml b/vaadin-testbench-unit-junit5/pom.xml
index e16a948bd..6dcbc33d9 100644
--- a/vaadin-testbench-unit-junit5/pom.xml
+++ b/vaadin-testbench-unit-junit5/pom.xml
@@ -324,6 +324,17 @@
provided
true
+
+ org.slf4j
+ slf4j-simple
+ test
+
+
+ com.vaadin
+ uiunit-component-testers
+ ${project.version}
+ test
+
diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java
index 53432ef16..000a741e8 100644
--- a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java
+++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/charts/ChartTesterTest.java
@@ -13,6 +13,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import com.vaadin.testbench.unit.CommercialTesterWrappers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -24,7 +25,7 @@
import com.vaadin.testbench.unit.ViewPackages;
@ViewPackages
-class ChartTesterTest extends UIUnitTest {
+class ChartTesterTest extends UIUnitTest implements CommercialTesterWrappers {
ColumnChartView view;
diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java
new file mode 100644
index 000000000..b636ba7e8
--- /dev/null
+++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/testbench/unit/TesterScanTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2000-2025 Vaadin Ltd
+ *
+ * This program is available under Vaadin Commercial License and Service Terms.
+ *
+ * See for the full
+ * license.
+ */
+
+package com.vaadin.testbench.unit;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+// Ensure that scanning testers does not fail when a tester references
+// a component class that is not present on the classpath.
+public class TesterScanTest {
+
+ @Test
+ public void scanForTesters_testerForClassNotInClasspath_doNotThrowOnClassNotFoundException() {
+ // Loads a dummy tester annotated with @Tests using an FQN to a
+ // non-existing component class.
+ Assertions.assertDoesNotThrow(() -> BaseUIUnitTest
+ .scanForTesters("com.vaadin.testbench.dontscan.classnotfound"));
+ }
+
+ @Test
+ public void scanForTesters_testerForClassNotInClasspath_doNotThrowNoClassDefFound() {
+ // Loads a dummy tester annotated with @Tests referencing a class in
+ // another module with provided scope so the test itself is not able to
+ // load the class.
+ Assertions.assertDoesNotThrow(() -> BaseUIUnitTest.scanForTesters(
+ "com.vaadin.testbench.dontscan.noclassdeffound"));
+ }
+
+ @Test
+ public void scanForTesters_testerForClassNotInClasspath_doNotThrowTypeNotPresentException() {
+ // Loads a dummy tester annotated with @Tests referencing a class in
+ // another module with provided scope so the test itself is not able to
+ // load the class.
+ Assertions.assertDoesNotThrow(() -> BaseUIUnitTest.scanForTesters(
+ "com.vaadin.testbench.dontscan.typenotpresent"));
+ }
+
+}
diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java
index 0e7be4209..ccb1fa960 100644
--- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java
+++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/BaseUIUnitTest.java
@@ -28,6 +28,7 @@
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.flow.component.Component;
@@ -63,6 +64,8 @@
*/
public abstract class BaseUIUnitTest {
+ private static final Logger LOGGER = LoggerFactory
+ .getLogger(BaseUIUnitTest.class.getPackageName());
private static final ConcurrentHashMap routesCache = new ConcurrentHashMap<>();
protected static final Map, Class extends ComponentTester>> testers = new HashMap<>();
@@ -85,7 +88,8 @@ public abstract class BaseUIUnitTest {
Capabilities.of(Capability.PRE_TRIAL));
}
- private static Map, Class extends ComponentTester>> scanForTesters(
+ // Visible for test
+ static Map, Class extends ComponentTester>> scanForTesters(
String... packages) {
try (ScanResult scan = new ClassGraph().enableClassInfo()
.enableAnnotationInfo().acceptPackages(packages).scan(2)) {
@@ -114,19 +118,39 @@ private static Map, Class extends ComponentTester>> scanForTesters(
try {
return UtilsKt.findClassOrThrow(clazz);
} catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
+ logTypeLoadingIssue(e,
+ "Tester '{}' cannot be loaded because of missing component class '{}' on classpath",
+ classInfo.getName(), clazz);
}
- }).forEach(clazz -> testerMap.put(clazz,
- (Class extends ComponentTester>) tester));
-
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
+ return null;
+ }).filter(Objects::nonNull)
+ .forEach(clazz -> testerMap.put(clazz,
+ (Class extends ComponentTester>) tester));
+
+ } catch (TypeNotPresentException e) {
+ logTypeLoadingIssue(e,
+ "Tester '{}' cannot be loaded because of missing class '{}' on classpath",
+ classInfo.getName(), e.typeName());
+ } catch (ClassNotFoundException
+ | NoClassDefFoundError e) {
+ logTypeLoadingIssue(e,
+ "Tester '{}' cannot be loaded because of missing class on classpath: {}",
+ classInfo.getName(), e.getMessage());
}
});
return Collections.unmodifiableMap(testerMap);
}
}
+ private static void logTypeLoadingIssue(Throwable ex, String message,
+ Object... args) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(message, args, ex);
+ } else {
+ LOGGER.warn(message, args);
+ }
+ }
+
protected synchronized Routes discoverRoutes() {
return discoverRoutes(scanPackages());
}
diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java
similarity index 94%
rename from vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java
rename to vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java
index 5541ee504..2baf581c9 100644
--- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTestWrappers.java
+++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/CommercialTesterWrappers.java
@@ -16,7 +16,7 @@
* Provides factory method to create testers for commercial components.
*/
@SuppressWarnings("unchecked")
-public interface CommercialTestWrappers {
+public interface CommercialTesterWrappers {
/**
* Create a tester for the given Chart instance.
diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java
index 8680fac39..b26711daa 100644
--- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java
+++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java
@@ -14,8 +14,6 @@
import com.vaadin.flow.component.accordion.AccordionTester;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonTester;
-import com.vaadin.flow.component.charts.Chart;
-import com.vaadin.flow.component.charts.ChartTester;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.checkbox.CheckboxGroupTester;
@@ -138,18 +136,6 @@ default ButtonTester