java Spring的测试注解@Sql如何表现得像@BeforeClass?

plupiseo  于 2023-01-29  发布在  Java
关注(0)|答案(6)|浏览(160)

我如何告诉@Sql注解只为类运行一次,而不是为每个@Test方法运行一次?
就像@BeforeClass一样?

@org.springframework.test.context.jdbc.Sql(
     scripts = "classpath:schema-test.sql",
     executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
public class TestClass {
      @Test
      public void test1() {
        //runs the @Sql script
      }

      @Test
      public void test2() {
        //runs the @Sql script again
      }
}
hc8w905p

hc8w905p1#

对于JUnit 5,直接的清洁*解决方案 *:

@MyInMemoryDbConfig
//@Sql(value = {"/appconfig.sql", "/album.sql"}) -> code below is equivalent but at class level
class SomeServiceTest {
    @BeforeAll
    void setup(@Autowired DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {
            // you'll have to make sure conn.autoCommit = true (default for e.g. H2)
            // e.g. url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;MODE=MySQL
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("appconfig.sql"));
            ScriptUtils.executeSqlScript(conn, new ClassPathResource("album.sql"));
        }
    }
    // your @Test methods follow ...

但是,如果数据库连接没有配置为autoCommit = true,则必须将所有连接都 Package 在一个事务中:

@RootInMemoryDbConfig
@Slf4j
class SomeServiceTest {
    @BeforeAll
    void setup(@Autowired DataSource dataSource,
            @Autowired PlatformTransactionManager transactionManager) {
        new TransactionTemplate(transactionManager).execute((ts) -> {
            try (Connection conn = dataSource.getConnection()) {
                ScriptUtils.executeSqlScript(conn, new ClassPathResource("appconfig.sql"));
                ScriptUtils.executeSqlScript(conn, new ClassPathResource("album.sql"));
                // should work without manually commit but didn't for me (because of using AUTOCOMMIT=OFF)
                // I use url=jdbc:h2:mem:myDb;DB_CLOSE_DELAY=-1;MODE=MySQL;AUTOCOMMIT=OFF
                // same will happen with DataSourceInitializer & DatabasePopulator (at least with this setup)
                conn.commit();
            } catch (SQLException e) {
                SomeServiceTest.log.error(e.getMessage(), e);
            }
            return null;
        });
    }
    // your @Test methods follow ...

为什么清洁*溶液 *?
因为根据使用@SqlConfig的脚本配置:
@Sql和@SqlConfig提供的配置选项与ScriptUtils和ResourceDatabasePopulator支持的配置选项等效,但它们是XML命名空间元素提供的配置选项的超集。

奖金

您可以将此方法与其他@Sql声明混合使用。

unftdfkk

unftdfkk2#

你不可能开箱即用,@Sql annotation只有两种模式-BEFORE_TEST_METHODAFTER_TEST_METHOD
负责执行这些脚本的侦听器SqlScriptsTestExecutionListener不实现类前或类后方法。
为了解决这个问题,我将实现自己的TestExecutionListener, Package 默认的SqlScriptsTestExecutionListener,然后您可以在测试中声明使用新的侦听器而不是旧的侦听器。

public class BeforeClassSqlScriptsTestExecutionListener implements TestExecutionListener
{    
    @Override
    public void beforeTestClass(final TestContext testContext) throws Exception
    {
        // Note, we're deliberately calling beforeTest*Method*
        new SqlScriptsTestExecutionListener().beforeTestMethod(testContext);
    }

    @Override
    public void prepareTestInstance(final TestContext testContext) { }

    @Override
    public void beforeTestMethod(final TestContext testContext) { }

    @Override
    public void afterTestMethod(final TestContext testContext) { }

    @Override
    public void afterTestClass(final TestContext testContext) { }
}

您的测试将变为:

@TestExecutionListeners(
    listeners = { BeforeClassSqlScriptsTestExecutionListener.class },
    /* Here, we're replacing more than just SqlScriptsTestExecutionListener, so manually
       include any of the default above if they're still needed: */
    mergeMode = TestExecutionListeners.MergeMode.REPLACE_DEFAULTS
)
@org.springframework.test.context.jdbc.Sql(
    scripts = "classpath:schema-test.sql",
    executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
public class MyTest
{
    @Test
    public void test1() { }

    @Test
    public void test2() { }
}
uinbv5nw

uinbv5nw3#

@BeforeAll和@BeforeClass注解需要“static”方法。所以它不起作用。配置文件中的@PostConstruct怎么样?它对我来说很好用。

@TestConfiguration
public class IntegrationTestConfiguration {

@Autowired
private DataSource dataSource;

@PostConstruct
public void initDB() throws SQLException {
    try (Connection con = dataSource.getConnection()) {
        ScriptUtils.executeSqlScript(con, new ClassPathResource("data.sql"));
    }
}
}

@ContextConfiguration(classes = {IntegrationTestConfiguration.class})
public class YourIntegrationTest {

}
jtw3ybtb

jtw3ybtb4#

由于DefaultTestContext.java中的getTestMethod()方法,这段代码抛出了一个IllegalStateException(Spring 5.0.1):

public final Method getTestMethod() {
    Method testMethod = this.testMethod;
    Assert.state(testMethod != null, "No test method");
    return testMethod;
}

当通过您建议的实现调用beforeTestClass方法时,textContext不包含有效的testMethod(这在此阶段是正常的):

public class BeforeClassSqlScriptsTestExecutionListener implements TestExecutionListener {

    @Override
    public void beforeTestClass(TestContext testContext) throws Exception {
        new SqlScriptsTestExecutionListener().beforeTestMethod(testContext);
    }
}

执行负责运行SQL脚本的代码(在SqlScriptsTestExecutionListener中)时,需要有效的testMethod

Set<Sql> sqlAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
            testContext.getTestMethod(), Sql.class, SqlGroup.class);

我最终使用了以下解决方案:

@Before
public void setUp() {
    // Manually initialize DB as @Sql annotation doesn't support class-level execution phase (actually executed before every test method)
    // See https://jira.spring.io/browse/SPR-14357
    if (!dbInitialized) {
        final ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
        resourceDatabasePopulator.addScript(new ClassPathResource("/sql/[...].sql"));
        resourceDatabasePopulator.execute(dataSource);
        dbInitialized = true;
    }
    [...]
}
h9a6wy2h

h9a6wy2h5#

对于JUnit 5,我支持adrhc的解决方案。
对于Junit 4,您可以执行以下操作:

@Autowired
private DataSource database;

private static boolean dataLoaded = false;

    @Before
    public void setup() throws SQLException {
        if(!dataLoaded) {
            try (Connection con = database.getConnection()) {
                ScriptUtils.executeSqlScript(con, new ClassPathResource("path/to/script.sql"));
                dataLoaded = true;
            }
        }
    }

(同样,假设您的连接是autoCommit=true,请参见adrhc的帖子。)
如果您打算并行运行测试,那么您需要使方法同步。

tjvv9vkg

tjvv9vkg6#

如果不会导致不可接受的测试运行时间,另一种方法是对每种方法进行设置和拆卸。

@Sql({"/setup.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql({"/teardown.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)

另一种方法是将测试数据放入以下文件中,如https://www.baeldung.com/spring-boot-data-sql-and-schema-sql所述:

  • src/test/resources/schema.sql
  • src/test/resources/data.sql

相关问题