1. Project Clover database Wed Nov 12 2025 05:07:35 UTC
  2. Package guru.mikelue.foxglove.springframework

File FoxgloveTestListener.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
53% of files have more coverage

Code metrics

2
19
8
2
205
121
10
0.53
2.38
4
1.25

Classes

Class Line # Actions
FoxgloveTestListener 70 16 0% 8 5
0.791666779.2%
SpringContextGenDataProcessor 176 3 0% 2 0
1.0100%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    package guru.mikelue.foxglove.springframework;
2   
3    import java.util.function.Supplier;
4   
5    import javax.sql.DataSource;
6   
7    import org.springframework.beans.factory.BeanFactory;
8    import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
9    import org.springframework.context.ApplicationContext;
10    import org.springframework.jdbc.datasource.DataSourceUtils;
11    import org.springframework.test.context.NestedTestConfiguration;
12    import org.springframework.test.context.TestContext;
13    import org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener;
14    import org.springframework.test.context.support.AbstractTestExecutionListener;
15    import org.springframework.test.context.transaction.TestTransaction;
16   
17    import org.slf4j.Logger;
18    import org.slf4j.LoggerFactory;
19   
20    import guru.mikelue.foxglove.DataGenerator;
21    import guru.mikelue.foxglove.TableFacet;
22    import guru.mikelue.foxglove.annotation.*;
23    import guru.mikelue.foxglove.functional.DataGeneratorProvider;
24    import guru.mikelue.foxglove.functional.TableFacetsProvider;
25    import guru.mikelue.foxglove.jdbc.JdbcDataGenerator;
26   
27    /**
28    * This listener resolving {@link TableFacetsSource} and {@link DataGeneratorSource}
29    * in Spring's Context framework of test first.
30    *
31    * <h2>From Spring's context</h2>
32    *
33    * <ol>
34    * <li>For named sources, using {@link BeanFactory#getBean(String)} to get object</li>
35    * <li>For subtype of {@link TableFacetsProvider} or {@link DataGeneratorProvider}, using {@link BeanFactory#getBean(Class)} to get object of that type.</li>
36    * <li>For {@link DataGenerator}, using {@link BeanFactory#getBean(Class)} with type of
37    * {@link DataGeneratorProvider TableFacetsProvider.class}, or {@link DataGenerator DataGenerator.class}.<br>
38    * This method would throw {@link NoUniqueBeanDefinitionException} if there are multiple candidates in the context.
39    * </li>
40    * <li>For {@link TableFacet}, using {@link BeanFactory#getBean(Class)} with type of
41    * {@link TableFacetsProvider TableFacetsProvider.class}.<br>
42    * This method would throw {@link NoUniqueBeanDefinitionException} if there are multiple candidates in the context.
43    * </li>
44    * </ol>
45    *
46    * <h3>Auto-constructing default {@link DataGenerator}</h3>
47    *
48    * If the Spring's context cannot find a viable {@link DataGenerator} bean,
49    * this listener will construct with:
50    *
51    * <ol>
52    * <li>Using {@link TestTransaction#isActive} and {@link DataSourceUtils#getConnection(DataSource)} to get current transaction's connection for data generation.</li>
53    * <li>Otherwise, using {@link DataSource} to construct a new {@link DataGenerator}(prototype scope).</li>
54    * </ol>
55    *
56    * <p>
57    * Otherwise, this listener follows the semantics defined by {@link guru.mikelue.foxglove.annotation annotation doc}.
58    *
59    * <h2>Nested test classes</h2>
60    *
61    * <p>
62    * This listener doesn't support resolving sources from enclosing classes.<br>
63    * However, you could use Spring's {@link NestedTestConfiguration} to resolve sources from Spring's context.
64    *
65    * @see JdbcDataGenerator
66    * @see TableFacetsSource
67    * @see DataGeneratorSource
68    * @see NestedTestConfiguration
69    */
 
70    public class FoxgloveTestListener extends AbstractTestExecutionListener {
71    private final Logger logger = LoggerFactory.getLogger(FoxgloveTestListener.class);
72   
73    /**
74    * The default order of this listener is just after {@link SqlScriptsTestExecutionListener}.
75    */
76    public final static int ORDER = 5000 + 1;
77   
78    private final static String ATTR_DATA_GEN_CONTEXT_PREFIX = "foxglove.data_gen_context.";
79    private final static String ATTR_CLASS_LEVEL_PREFIX = "foxglove.class_level.";
80   
81    /**
82    * Default constructor.
83    */
 
84  6 toggle public FoxgloveTestListener() {}
85   
86    /**
87    * The order of this listener is just after {@link SqlScriptsTestExecutionListener}.
88    *
89    * @return The order value
90    *
91    * @see #ORDER
92    */
 
93  40 toggle @Override
94    public int getOrder()
95    {
96  40 return ORDER;
97    }
98   
 
99  4 toggle @Override
100    public void beforeTestClass(TestContext testContext) throws Exception
101    {
102  4 testContext.computeAttribute(
103    keyForDataGenContext(testContext.getTestClass()),
104    clazz -> DataGenContext.loadFrom(
105    testContext.getTestClass()
106    )
107    );
108    }
109   
 
110  7 toggle @SuppressWarnings("unchecked")
111    @Override
112    public void beforeTestMethod(TestContext testContext) throws Exception
113    {
114  7 Supplier<DataGenContext<TableFacet>> contextSupplier = () ->
115    (DataGenContext<TableFacet>)testContext.getAttribute(
116    keyForDataGenContext(testContext.getTestClass())
117    );
118   
119    /*
120    * Perform data generation on class level only once.
121    */
122  7 var classLevelAttrName = ATTR_CLASS_LEVEL_PREFIX + testContext.getTestClass().getCanonicalName();
123  7 testContext.computeAttribute(
124    classLevelAttrName,
125    name -> doOnClassLevel(
126    testContext, contextSupplier
127    )
128    );
129    // :~)
130   
131  7 new SpringContextGenDataProcessor(
132    testContext.getApplicationContext(),
133    testContext.getTestInstance(), testContext.getTestMethod(),
134    contextSupplier,
135    testContext.getTestMethod().getName()
136    )
137    .performGenerating();
138    }
139   
 
140  3 toggle private Integer doOnClassLevel(
141    TestContext testContext, Supplier<DataGenContext<TableFacet>> dataGenContextSupplier
142    ) {
143  3 var testingInstance = testContext.getTestInstance();
144  3 var testingClass = testContext.getTestClass();
145   
146  3 var processor = new SpringContextGenDataProcessor(
147    testContext.getApplicationContext(),
148    testingInstance, testingClass, dataGenContextSupplier,
149    testingClass.getSimpleName()
150    );
151   
152  3 if (!processor.hasDataGenerating()) {
153  3 return -1;
154    }
155   
156  0 logger.debug("Performing data generation on class level: \"{}\"",
157    testingInstance.getClass().getSimpleName()
158    );
159   
160  0 try {
161  0 return processor.performGenerating();
162    } catch (Exception e) {
163  0 throw new RuntimeException(
164    "Failed to generating data for class level: " + testingInstance.getClass().getSimpleName(),
165    e
166    );
167    }
168    }
169   
 
170  11 toggle private static String keyForDataGenContext(Class<?> testedClass)
171    {
172  11 return ATTR_DATA_GEN_CONTEXT_PREFIX + testedClass.getCanonicalName();
173    }
174    }
175   
 
176    class SpringContextGenDataProcessor extends GenDataProcessor {
177    private final ApplicationContext appContext;
178   
 
179  10 toggle public SpringContextGenDataProcessor(
180    ApplicationContext appContext,
181    Object testingInstance, java.lang.reflect.AnnotatedElement annotatedElement,
182    Supplier<DataGenContext<TableFacet>> dataGenContextSupplier,
183    String name
184    ) {
185  10 super(
186    testingInstance, annotatedElement,
187    dataGenContextSupplier, name
188    );
189   
190  10 this.appContext = appContext;
191    }
192   
 
193  7 toggle @Override
194    protected GenDataObjectFactory buildObjectFactory(
195    Object testingInstance, GenData genData,
196    DataGenContext<TableFacet> dataGenContext,
197    String name
198    ) {
199  7 return new SpringContextObjectFactory(
200    appContext,
201    testingInstance, genData,
202    dataGenContext, name
203    );
204    }
205    }