From b08654c68b7f50f5ea6e53139503969666cafbfb Mon Sep 17 00:00:00 2001 From: CodeCaster Date: Thu, 17 Jul 2025 15:55:17 +0800 Subject: [PATCH] [fit] optimize test framework to support executing specific actions at test startup --- .../fitframework/test/annotation/Sql.java | 29 ++++++++- .../fitframework/test/domain/TestPlugin.java | 25 ++++++-- .../domain/listener/DataSourceListener.java | 30 +++++----- .../test/domain/listener/MockMvcListener.java | 8 ++- .../domain/listener/SqlExecuteListener.java | 59 +++++++++++++++---- .../resolver/DefaultTestClassResolver.java | 10 ++-- .../DefaultTestContextConfiguration.java | 47 +++++++++++---- .../resolver/TestContextConfiguration.java | 32 ++++++++-- .../integration/mybatis/WordMapperTest.java | 2 +- 9 files changed, 184 insertions(+), 58 deletions(-) diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/annotation/Sql.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/annotation/Sql.java index d6ac89f5..04281151 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/annotation/Sql.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/annotation/Sql.java @@ -15,15 +15,38 @@ * 用于在测试用例前执行初始化 Sql 语句。 * * @author 易文渊 + * @author 季聿阶 * @since 2024-07-21 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Sql { /** - * 获取 sql 脚本文件路径。 + * 获取前置 SQL 脚本文件路径。 * - * @return 表示 sql 脚本文件路径集合的 {@code String[]}。 + * @return 表示前置 SQL 脚本文件路径集合的 {@link String}{@code []}。 */ - String[] scripts(); + String[] before() default {}; + + /** + * 获取后置 SQL 脚本文件路径。 + * + * @return 表示后置 SQL 脚本文件路径集合的 {@link String}{@code []}。 + */ + String[] after() default {}; + + /** + * 获取 SQL 脚本执行位置。 + */ + enum Position { + /** + * 在前置执行。 + */ + BEFORE, + + /** + * 在后置执行。 + */ + AFTER + } } diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/TestPlugin.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/TestPlugin.java index 054037e5..088ba9c1 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/TestPlugin.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/TestPlugin.java @@ -27,7 +27,9 @@ import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -64,7 +66,7 @@ public TestPlugin(FitRuntime runtime, TestContextConfiguration configuration) { Validation.notNull(configuration, "The configuration to create test plugin cannot be null."); this.packageScanner = this.scanner((packageScanner, clazz) -> this.onClassDetected(packageScanner, clazz, // 包含的类已经提前注册,因此需要将包含的和排除的类进行合并。 - Stream.concat(Arrays.stream(this.configuration.includeClasses()), + Stream.concat(this.configuration.includeClasses().keySet().stream(), Arrays.stream(this.configuration.excludeClasses())).collect(Collectors.toSet()))); } @@ -93,6 +95,7 @@ protected void registerSystemBeans() { @Override protected void scanBeans() { this.registerBeans(this.configuration.includeClasses()); + this.configuration.actions().forEach(action -> action.accept(this)); this.scan(this.configuration.scannedPackages()); this.registerMockedBeans(this.configuration.mockedBeanFields()); } @@ -111,10 +114,22 @@ private void onClassDetected(PackageScanner scanner, Class clazz, Set[] classArray) { - Arrays.stream(classArray) - .filter(clazz -> !this.container().lookup(clazz).isPresent()) - .forEach(clazz -> this.container().registry().register(clazz)); + private void registerBeans(Map, Supplier> classes) { + classes.entrySet() + .stream() + .filter(entry -> this.container().lookup(entry.getKey()).isEmpty()) + .forEach(entry -> { + if (entry.getValue() == null) { + this.container().registry().register(entry.getKey()); + } else { + Object bean = entry.getValue().get(); + if (bean == null) { + this.container().registry().register(entry.getKey()); + } else { + this.container().registry().register(bean); + } + } + }); } private void scan(Set basePackages) { diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/DataSourceListener.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/DataSourceListener.java index 001fa8f9..828905f7 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/DataSourceListener.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/DataSourceListener.java @@ -6,15 +6,15 @@ package modelengine.fitframework.test.domain.listener; -import modelengine.fitframework.ioc.BeanContainer; -import modelengine.fitframework.ioc.BeanNotFoundException; import modelengine.fitframework.test.annotation.EnableDataSource; -import modelengine.fitframework.test.domain.TestContext; +import modelengine.fitframework.test.domain.resolver.TestContextConfiguration; import modelengine.fitframework.test.domain.util.AnnotationUtils; +import modelengine.fitframework.util.MapBuilder; import org.h2.jdbcx.JdbcConnectionPool; import java.util.Optional; +import java.util.function.Supplier; import javax.sql.DataSource; @@ -22,23 +22,23 @@ * 用于注入 dataSource 的监听器。 * * @author 易文渊 + * @author 季聿阶 * @since 2024-07-21 */ public class DataSourceListener implements TestListener { @Override - public void beforeTestClass(TestContext context) { - Class clazz = context.testClass(); + public Optional config(Class clazz) { Optional annotationOption = AnnotationUtils.getAnnotation(clazz, EnableDataSource.class); - if (!annotationOption.isPresent()) { - return; - } - BeanContainer beanContainer = context.plugin().container(); - try { - beanContainer.beans().get(DataSource.class); - } catch (BeanNotFoundException e) { - EnableDataSource enableDataSource = annotationOption.get(); - DataSource dataSource = JdbcConnectionPool.create(enableDataSource.model().getUrl(), "sa", "sa"); - beanContainer.registry().register(dataSource); + if (annotationOption.isEmpty()) { + return Optional.empty(); } + TestContextConfiguration customConfig = TestContextConfiguration.custom() + .testClass(clazz) + .includeClasses(MapBuilder., Supplier>get().put(DataSource.class, () -> { + EnableDataSource enableDataSource = annotationOption.get(); + return JdbcConnectionPool.create(enableDataSource.model().getUrl(), "sa", "sa"); + }).build()) + .build(); + return Optional.of(customConfig); } } \ No newline at end of file diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/MockMvcListener.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/MockMvcListener.java index 078bc032..ea1c7d93 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/MockMvcListener.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/MockMvcListener.java @@ -17,6 +17,7 @@ import modelengine.fitframework.test.domain.mvc.request.MockRequestBuilder; import modelengine.fitframework.test.domain.resolver.TestContextConfiguration; import modelengine.fitframework.test.domain.util.AnnotationUtils; +import modelengine.fitframework.util.MapBuilder; import modelengine.fitframework.util.StringUtils; import modelengine.fitframework.util.ThreadUtils; @@ -26,6 +27,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; /** * 用于注入 mockMvc 的监听器。 @@ -50,12 +52,12 @@ public MockMvcListener(int port) { @Override public Optional config(Class clazz) { - if (!AnnotationUtils.getAnnotation(clazz, EnableMockMvc.class).isPresent()) { + if (AnnotationUtils.getAnnotation(clazz, EnableMockMvc.class).isEmpty()) { return Optional.empty(); } TestContextConfiguration configuration = TestContextConfiguration.custom() .testClass(clazz) - .includeClasses(new Class[] {MockController.class}) + .includeClasses(MapBuilder., Supplier>get().put(MockController.class, null).build()) .scannedPackages(DEFAULT_SCAN_PACKAGES) .build(); return Optional.of(configuration); @@ -64,7 +66,7 @@ public Optional config(Class clazz) { @Override public void beforeTestClass(TestContext context) { Class testClass = context.testClass(); - if (!AnnotationUtils.getAnnotation(testClass, EnableMockMvc.class).isPresent()) { + if (AnnotationUtils.getAnnotation(testClass, EnableMockMvc.class).isEmpty()) { return; } MockMvc mockMvc = new MockMvc(this.port); diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/SqlExecuteListener.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/SqlExecuteListener.java index 4488d5c1..6c277404 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/SqlExecuteListener.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/listener/SqlExecuteListener.java @@ -6,14 +6,18 @@ package modelengine.fitframework.test.domain.listener; +import modelengine.fitframework.plugin.Plugin; import modelengine.fitframework.test.annotation.Sql; import modelengine.fitframework.test.domain.TestContext; +import modelengine.fitframework.test.domain.resolver.TestContextConfiguration; import modelengine.fitframework.util.IoUtils; import java.io.IOException; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.SQLException; +import java.util.List; +import java.util.Optional; import javax.sql.DataSource; @@ -21,6 +25,7 @@ * 用于执行 SQL 脚本。 * * @author 易文渊 + * @author 季聿阶 * @since 2024-07-21 */ public class SqlExecuteListener implements TestListener { @@ -29,34 +34,66 @@ public class SqlExecuteListener implements TestListener { private Sql globalSql; @Override - public void beforeTestClass(TestContext context) { - Class testClass = context.testClass(); - this.globalSql = testClass.getAnnotation(Sql.class); + public Optional config(Class clazz) { + this.globalSql = clazz.getAnnotation(Sql.class); + if (this.globalSql == null) { + return Optional.empty(); + } + TestContextConfiguration configuration = + TestContextConfiguration.custom().testClass(clazz).actions(List.of(this::executeAction)).build(); + return Optional.of(configuration); + } + + private void executeAction(Plugin plugin) { + if (this.globalSql == null) { + return; + } + executeSql(plugin, this.globalSql, Sql.Position.BEFORE); } @Override public void beforeTestMethod(TestContext context) { - execSql(globalSql, context); - execMethodSql(context); + execMethodSql(context, Sql.Position.BEFORE); + } + + @Override + public void afterTestMethod(TestContext context) { + execMethodSql(context, Sql.Position.AFTER); } - private static void execMethodSql(TestContext context) { + private static void execMethodSql(TestContext context, Sql.Position position) { Method method = context.testMethod(); Sql sql = method.getAnnotation(Sql.class); - execSql(sql, context); + executeSql(context.plugin(), sql, position); + } + + @Override + public void afterTestClass(TestContext context) { + Class testClass = context.testClass(); + Sql sql = testClass.getAnnotation(Sql.class); + executeSql(context.plugin(), sql, Sql.Position.AFTER); } - private static void execSql(Sql sql, TestContext context) { + private static void executeSql(Plugin plugin, Sql sql, Sql.Position position) { if (sql == null) { return; } - DataSource dataSource = context.plugin().container().beans().get(DataSource.class); + DataSource dataSource = plugin.container().beans().get(DataSource.class); try (Connection connection = dataSource.getConnection()) { - for (String script : sql.scripts()) { + String[] scripts = getScripts(sql, position); + for (String script : scripts) { connection.createStatement().execute(IoUtils.content(CLASS_LOADER, script)); } } catch (SQLException | IOException e) { - throw new IllegalStateException("Fail to execute sql.", e); + throw new IllegalStateException("Failed to execute sql.", e); + } + } + + private static String[] getScripts(Sql sql, Sql.Position position) { + if (position == Sql.Position.BEFORE) { + return sql.before(); + } else { + return sql.after(); } } } \ No newline at end of file diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestClassResolver.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestClassResolver.java index 3ba6641e..208153d0 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestClassResolver.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestClassResolver.java @@ -21,7 +21,9 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 默认的单测类解析器。 @@ -30,8 +32,7 @@ * @since 2023-01-17 */ public class DefaultTestClassResolver implements TestClassResolver { - private static final Set DEFAULT_SCAN_PACKAGES = new HashSet<>(Arrays.asList( - "modelengine.fit.value", + private static final Set DEFAULT_SCAN_PACKAGES = new HashSet<>(Arrays.asList("modelengine.fit.value", "modelengine.fit.serialization", "modelengine.fitframework.validation")); @@ -41,7 +42,8 @@ public TestContextConfiguration resolve(Class clazz) { Class[] includeClasses = this.resolveIncludeClasses(testConfigurationClass); return TestContextConfiguration.custom() .testClass(clazz) - .includeClasses(includeClasses) + .includeClasses(Stream.of(includeClasses) + .collect(Collectors.toMap(Function.identity(), key -> () -> null))) .excludeClasses(this.resolveExcludeClasses(clazz)) .scannedPackages(this.scanBeans(includeClasses)) .mockedBeanFields(this.scanMockBeansFieldSet(clazz)) @@ -86,7 +88,7 @@ private Set scanBeans(Class[] classes) { private Set getBasePackages(Class clazz) { Optional opScanPackagesAnnotation = AnnotationUtils.getAnnotation(clazz, ScanPackages.class); - if (!opScanPackagesAnnotation.isPresent()) { + if (opScanPackagesAnnotation.isEmpty()) { return new HashSet<>(); } Set basePackages = new HashSet<>(Arrays.asList(opScanPackagesAnnotation.get().value())); diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestContextConfiguration.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestContextConfiguration.java index 75abdb2d..50752e4f 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestContextConfiguration.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/DefaultTestContextConfiguration.java @@ -9,15 +9,20 @@ import static modelengine.fitframework.inspection.Validation.notNull; import modelengine.fitframework.inspection.Validation; +import modelengine.fitframework.plugin.Plugin; import modelengine.fitframework.util.ObjectUtils; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Supplier; /** * 默认的测试上下文的配置类。 @@ -28,32 +33,36 @@ */ public class DefaultTestContextConfiguration implements TestContextConfiguration { private final Class testClass; - private final List> includeClasses; + private final Map, Supplier> includeClasses; private final List> excludeClasses; private final Set scannedPackages; private final Set mockedBeanFields; private final Set> toSpyClasses; + private final List> actions; /** * 默认测试上下文的配置类构造函数。 * * @param testClass 表示测试类的 {@link Class}。 - * @param includeClasses 表示注入 Bean 类型数组的 {@code Class[]}。 + * @param includeClasses 表示注入 Bean 类型数组的 {@link Map}{@code <}{@link Class}{@code , }{@link Supplier}{@code + * <}{@link Object}{@code >>}。 * @param excludeClasses 表示排除 Bean 类型数组的 {@code Class[]}。 * @param scannedPackages 表示扫描包路径的 {@link Set}{@code <}{@link String}{@code >}。 * @param mockedBeanFields 表示 mocked bean 字段的 {@link Set}{@code <}{@link Field}{@code >}。 * @param toSpyClasses 表示需要侦听的类集合的 {@link Set}{@code <}{@link Class}{@code >}。 + * @param actions 测试类初始化时执行的操作的 {@link List}{@code <}{@link Consumer}{@code <}{@link Plugin}{@code >>}。 */ - public DefaultTestContextConfiguration(Class testClass, Class[] includeClasses, Class[] excludeClasses, - Set scannedPackages, Set mockedBeanFields, Set> toSpyClasses) { + public DefaultTestContextConfiguration(Class testClass, Map, Supplier> includeClasses, + Class[] excludeClasses, Set scannedPackages, Set mockedBeanFields, + Set> toSpyClasses, List> actions) { this.testClass = notNull(testClass, "The test class cannot be null."); - this.includeClasses = - new ArrayList<>(includeClasses == null ? Collections.emptyList() : Arrays.asList(includeClasses)); + this.includeClasses = includeClasses; this.excludeClasses = new ArrayList<>(excludeClasses == null ? Collections.emptyList() : Arrays.asList(excludeClasses)); this.scannedPackages = notNull(scannedPackages, "The scanned packages cannot be null."); this.mockedBeanFields = notNull(mockedBeanFields, "The mocked bean fields cannot be null."); this.toSpyClasses = ObjectUtils.nullIf(toSpyClasses, Collections.emptySet()); + this.actions = notNull(actions, "The actions cannot be null."); } @Override @@ -62,8 +71,8 @@ public Class testClass() { } @Override - public Class[] includeClasses() { - return this.includeClasses.toArray(new Class[0]); + public Map, Supplier> includeClasses() { + return this.includeClasses; } @Override @@ -86,14 +95,20 @@ public Set> toSpyClasses() { return Collections.unmodifiableSet(this.toSpyClasses); } + @Override + public List> actions() { + return Collections.unmodifiableList(this.actions); + } + @Override public void merge(TestContextConfiguration configuration) { Validation.equals(this.testClass, configuration.testClass(), "The test class must equal"); - this.includeClasses.addAll(Arrays.asList(configuration.includeClasses())); + this.includeClasses.putAll(configuration.includeClasses()); this.excludeClasses.addAll(Arrays.asList(configuration.excludeClasses())); this.scannedPackages.addAll(configuration.scannedPackages()); this.mockedBeanFields.addAll(configuration.mockedBeanFields()); this.toSpyClasses.addAll(configuration.toSpyClasses()); + this.actions.addAll(configuration.actions()); } @Override @@ -106,11 +121,12 @@ public int hashCode() { */ public static final class Builder implements TestContextConfiguration.Builder { private Class testClass; - private Class[] includeClasses; + private Map, Supplier> includeClasses = new HashMap<>(); private Class[] excludeClasses; private Set scannedPackages = new HashSet<>(); private Set mockedBeanFields = new HashSet<>(); private Set> toSpyClasses = new HashSet<>(); + private List> actions = new ArrayList<>(); @Override public TestContextConfiguration.Builder testClass(Class testClass) { @@ -119,7 +135,7 @@ public TestContextConfiguration.Builder testClass(Class testClass) { } @Override - public TestContextConfiguration.Builder includeClasses(Class[] classes) { + public TestContextConfiguration.Builder includeClasses(Map, Supplier> classes) { this.includeClasses = classes; return this; } @@ -148,6 +164,12 @@ public TestContextConfiguration.Builder toSpyClasses(Set> toSpyClasses) return this; } + @Override + public TestContextConfiguration.Builder actions(List> actions) { + this.actions = actions; + return this; + } + @Override public TestContextConfiguration build() { return new DefaultTestContextConfiguration(this.testClass, @@ -155,7 +177,8 @@ public TestContextConfiguration build() { this.excludeClasses, this.scannedPackages, this.mockedBeanFields, - this.toSpyClasses); + this.toSpyClasses, + this.actions); } } } diff --git a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/TestContextConfiguration.java b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/TestContextConfiguration.java index ce1cbd17..6606d575 100644 --- a/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/TestContextConfiguration.java +++ b/framework/fit/java/fit-test/fit-test-framework/src/main/java/modelengine/fitframework/test/domain/resolver/TestContextConfiguration.java @@ -6,14 +6,21 @@ package modelengine.fitframework.test.domain.resolver; +import modelengine.fitframework.plugin.Plugin; + import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Supplier; /** * 测试上下文的配置类。 * * @author 邬涨财 * @author 易文渊 + * @author 季聿阶 * @since 2023-01-20 */ public interface TestContextConfiguration { @@ -27,9 +34,10 @@ public interface TestContextConfiguration { /** * 获取需要向容器上下文注入的类对象列表。 * - * @return 表示需要向容器上下文注入的类对象列表的 {@link Class}{@code []}。 + * @return 表示需要向容器上下文注入的类对象列表的 {@link Map}{@code <}{@link Class}{@code , }{@link Supplier}{@code + * <}{@link Object}{@code >>},其中,值为生成该类型对象的方法。 */ - Class[] includeClasses(); + Map, Supplier> includeClasses(); /** * 获取不需要向容器上下文注入的类对象列表。 @@ -59,6 +67,13 @@ public interface TestContextConfiguration { */ Set> toSpyClasses(); + /** + * 获取需要执行的操作列表。 + * + * @return 表示需要执行的操作列表的 {@link List}{@code <}{@link Consumer}{@code <}{@link Plugin}{@code >>}。 + */ + List> actions(); + /** * 合并另外一个 {@link TestContextConfiguration}。 * @@ -81,10 +96,11 @@ interface Builder { /** * 向当前构建器中设置需注入的类对象列表。 * - * @param classes 表示待设置的需注入的类对象列表的 {@link Class}{@code []}。 + * @param classes 表示待设置的需注入的类对象列表的 {@link Map}{@code <}{@link Class}{@code , }{@link + * Supplier}{@code <}{@link Object}{@code >>}。 * @return 表示当前构建器的 {@link Builder}。 */ - Builder includeClasses(Class[] classes); + Builder includeClasses(Map, Supplier> classes); /** * 向当前构建器中设置不需注入的类对象列表。 @@ -118,6 +134,14 @@ interface Builder { */ Builder toSpyClasses(Set> toSpyClasses); + /** + * 向当前构建器中设置需要执行操作列表。 + * + * @param actions 待设置的需要执行操作列表的 {@link List}{@code <}{@link Consumer}{@code <}{@link Plugin}{@code >>}。 + * @return 表示当前构建器的 {@link Builder}。 + */ + Builder actions(List> actions); + /** * 构建对象。 * diff --git a/framework/fit/java/integration/fit-mybatis/src/test/java/modelengine/fit/integration/mybatis/WordMapperTest.java b/framework/fit/java/integration/fit-mybatis/src/test/java/modelengine/fit/integration/mybatis/WordMapperTest.java index cca5f1f2..593b0ba6 100644 --- a/framework/fit/java/integration/fit-mybatis/src/test/java/modelengine/fit/integration/mybatis/WordMapperTest.java +++ b/framework/fit/java/integration/fit-mybatis/src/test/java/modelengine/fit/integration/mybatis/WordMapperTest.java @@ -25,7 +25,7 @@ * @since 2025-02-25 */ @MybatisTest(classes = {WordMapper.class}) -@Sql(scripts = "sql/create/word.sql") +@Sql(before = "sql/create/word.sql") @DisplayName("测试自动转换驼峰形式") public class WordMapperTest { @Fit