001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.test.generator.junit.parameterized; 017 018import de.cuioss.test.generator.TypedGenerator; 019import org.junit.jupiter.params.provider.ArgumentsSource; 020 021import java.lang.annotation.Documented; 022import java.lang.annotation.ElementType; 023import java.lang.annotation.Retention; 024import java.lang.annotation.RetentionPolicy; 025import java.lang.annotation.Target; 026 027/** 028 * {@code @CompositeTypeGeneratorSource} is an {@link ArgumentsSource} that provides 029 * access to values from multiple {@link TypedGenerator} implementations for 030 * parameterized tests, generating combinations of values. 031 * 032 * <p> 033 * This annotation allows you to combine multiple {@link TypedGenerator} implementations 034 * to generate combinations of values for your parameterized tests. The generators 035 * can be specified in three ways: 036 * </p> 037 * <ul> 038 * <li>By class using {@code generatorClasses}</li> 039 * <li>By method reference using {@code generatorMethods}</li> 040 * <li>By generator type using {@code generators} with {@link GeneratorType} enum values</li> 041 * </ul> 042 * 043 * <h2>Example Usage</h2> 044 * 045 * <pre> 046 * {@code 047 * // Using generator classes 048 * @ParameterizedTest 049 * @CompositeTypeGeneratorSource( 050 * generatorClasses = { 051 * NonBlankStringGenerator.class, 052 * IntegerGenerator.class 053 * }, 054 * count = 3 055 * ) 056 * void testWithMultipleGenerators(String text, Integer number) { 057 * assertNotNull(text); 058 * assertNotNull(number); 059 * // Test with combinations of text and number 060 * } 061 * 062 * // Using generator methods 063 * @ParameterizedTest 064 * @CompositeTypeGeneratorSource( 065 * generatorMethods = { 066 * "createStringGenerator", 067 * "createIntegerGenerator" 068 * }, 069 * count = 3 070 * ) 071 * void testWithMultipleMethodGenerators(String text, Integer number) { 072 * assertNotNull(text); 073 * assertNotNull(number); 074 * // Test with combinations of text and number 075 * } 076 * 077 * // Using generator types from the Generators class 078 * @ParameterizedTest 079 * @CompositeTypeGeneratorSource( 080 * generators = { 081 * GeneratorType.NON_EMPTY_STRINGS, 082 * GeneratorType.INTEGERS 083 * }, 084 * count = 3 085 * ) 086 * void testWithGeneratorTypes(String text, Integer number) { 087 * assertNotNull(text); 088 * assertNotNull(number); 089 * // Test with combinations of text and number 090 * } 091 * 092 * // Method generators 093 * static TypedGenerator<String> createStringGenerator() { 094 * return Generators.strings(5, 10); 095 * } 096 * 097 * static TypedGenerator<Integer> createIntegerGenerator() { 098 * return Generators.integers(1, 100); 099 * } 100 * } 101 * </pre> 102 * 103 * @author Oliver Wolff 104 * @since 2.0 105 * @see TypedGenerator 106 * @see CompositeTypeGeneratorArgumentsProvider 107 * @see GeneratorType 108 */ 109@Target({ElementType.METHOD}) 110@Retention(RetentionPolicy.RUNTIME) 111@Documented 112@ArgumentsSource(CompositeTypeGeneratorArgumentsProvider.class) 113public @interface CompositeTypeGeneratorSource { 114 115 /** 116 * The TypedGenerator classes to use for generating test values. 117 * Each class must have a no-args constructor. 118 * 119 * @return the TypedGenerator classes 120 */ 121 @SuppressWarnings("java:S1452") // This wildcard is because of the TypedGenerator interface. Ok for testing 122 Class<? extends TypedGenerator<?>>[] generatorClasses() default {}; 123 124 /** 125 * The method names to invoke to get TypedGenerator instances. 126 * Methods can be in the test class or in external classes using the format 127 * "fully.qualified.ClassName#methodName". 128 * 129 * @return the method names 130 */ 131 String[] generatorMethods() default {}; 132 133 /** 134 * The generator types to use from {@link de.cuioss.test.generator.Generators} class. 135 * This is a convenient way to use the standard generators without having to create 136 * custom generator classes or methods. 137 * 138 * @return the generator types 139 */ 140 GeneratorType[] generators() default {}; 141 142 /** 143 * Number of combinations to generate. 144 * For each generator, this many values will be generated and combined 145 * in a cartesian product with values from other generators. 146 * 147 * @return the number of values to generate per generator, defaults to 1 148 */ 149 int count() default 1; 150 151 /** 152 * Whether to generate a cartesian product of all generator values. 153 * If true, all possible combinations of values from the generators will be created. 154 * If false, generators will be paired one-to-one (requires all generators to produce 155 * the same number of values). 156 * 157 * @return true to generate a cartesian product, false for one-to-one pairing 158 */ 159 boolean cartesianProduct() default true; 160 161}