如何使用 Spring Test 为每个案例加载一次 DBUnit 测试数据

发布于 2024-09-05 03:30:47 字数 385 浏览 11 评论 0原文

Spring Test 有助于回滚测试方法中对数据库所做的任何更改。这意味着无需在每次测试方法之前花时间删除/重新加载测试数据。

但如果您使用 @BeforeClass Junit 注释,则会强制数据加载器为静态。这里探讨的问题是:为什么 jUnit 的fixtureSetup 必须是静态的?

如果数据初始化方法是静态的,因此数据连接方法和数据源也必须是静态的……等等……强制一切都是静态的……这是行不通的。此时,我会问 - 当您必须为每个测试删除/重新加载测试数据时,Spring Test 回滚更改的能力有什么好处??!?!

Spring Test helpfully rolls back any changes made to the database within a test method. This means that it is not necessary to take the time to delete/reload the test data before each test method.

But if you use the @BeforeClass Junit annotation, then that forces the data loader to be static. A question that is explored here: Why must jUnit's fixtureSetup be static?

If the data initialization method is static, so must the data connection methods and the data source..and on and on...forcing everything to be static...which won't work. At which point, I ask - what good is Spring Test's ability to rollback changes when you have to delete/reload the test data anyway for every test??!?!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小矜持 2024-09-12 03:30:47

一种可行的方法是创建一个“数据初始化程序”类,将其添加到也具有数据源的测试 Spring 应用程序上下文中,并将此应用程序上下文连接到您的测试中。这依赖于 Spring 在测试调用之间缓存应用程序上下文的事实。

例如,一个测试超类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:test-application-context.xml"})
@Transactional
public abstract class DataLoadingTest {
    @Autowired
    protected DatabaseInitialiser databaseInitialiser;
}

使用 test-application-context.xml

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dataSource" .../>

    <bean class="DatabaseInitialiser">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

并且

public class DatabaseInitialiser extends JdbcDaoSupport {
    @PostConstruct
    public void load() {
        // Initialise your database here: create schema, use DBUnit to load data, etc.
    }
}

在此示例中:

  • 所有依赖于数据库的测试都扩展 DataLoadingTest
  • Spring 在第一次测试调用时初始化应用程序上下文;
  • 这通过 @PostConstruct 注解调用 DatabaseInitialiser.load()
  • Spring 将应用程序上下文保存在缓存中;
  • 从已缓存的应用程序上下文中进一步测试 DatabaseInitialiser 中的调用;
  • 测试是事务性的,并在最后回滚到初始数据集。

同样,DatabaseInitialiser 可以有一个注释为 @PostDestroy 的方法,以在整个测试运行结束时执行任何必要的回滚。

One approach that works is to create a "data initialiser" class, add it to a test Spring application context that also has your data source, and wire this application context into your tests. This relies on the fact that Spring caches the application context between test invocations.

For example, a test superclass:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:test-application-context.xml"})
@Transactional
public abstract class DataLoadingTest {
    @Autowired
    protected DatabaseInitialiser databaseInitialiser;
}

With test-application-context.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dataSource" .../>

    <bean class="DatabaseInitialiser">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

And

public class DatabaseInitialiser extends JdbcDaoSupport {
    @PostConstruct
    public void load() {
        // Initialise your database here: create schema, use DBUnit to load data, etc.
    }
}

In this example:

  • all tests that rely on the database extend DataLoadingTest;
  • Spring initialises the application context upon first test invocation;
  • this calls DatabaseInitialiser.load(), via the @PostConstruct annotation;
  • Spring keeps the application context in a cache;
  • further test invocations wire in the DatabaseInitialiser from the application context, which is already cached;
  • tests are transactional, and roll back at the end to the initial data set.

Likewise, DatabaseInitialiser can have a method annotated @PostDestroy to perform any rollback necessary at the end of the whole test run.

困倦 2024-09-12 03:30:47

我们广泛地将 DBUnit 与 Spring Test 结合使用。但我们不使用 DBUnit 功能在测试结束时删除数据。

我们在 @Before 方法中为测试数据插入了一堆 DBUnit 来初始化测试。然后,当测试完成时,我们让 Spring 回滚功能将数据库恢复到原始状态。

我们遇到的最大问题是,必须在每次测试之前加载 DBUnit 数据,这可能会对性能造成重大影响。我们使用 DBUnit 的大多数测试都是只读的,根据某些预定义的行为来测试应用程序的行为。因此,我们习惯于创建主测试,然后在同一事务中批量运行所有细粒度测试。

We use DBUnit in conjunction with Spring Test extensively. But we do not use the DBUnit functionality to delete data at the end of the test.

We put a bunch of DBUnit inserts for our test data in the @Before method to initialise the test. Then when the test is complete we let the spring rollback functionality bring the database back to its original state.

The biggest problem we have with this is that the DBUnit data has to be loaded before each test, which can be a major performance hit. Most of our tests using DBUnit are read only, testing the behaviour of the application based on certain predefined behaviour. So we have a habit of creating master tests that then run all the fine grain tests in a batch within the same transaction.

瑾兮 2024-09-12 03:30:47

Spring Test 和 DbUnit 是两个优秀的框架。但将它们结合起来没有意义。由于 Spring Test 在连接上执行回滚,因此会在之后进行清理,而 DbUnit 会清理并将测试数据插入到 @Before 方法中。

如果您不依赖于任何动态数据,请使用 Spring,否则请使用 dbUnit。

Spring Test and DbUnit is two excellent frameworks. But it doesn't make sense to combine them. Since Spring Test execute a rollback on the connection, it cleans up afterwards, while DbUnit cleans up and insert test data in the @Before method.

Use Spring if you're not dependent on any dynamic data and dbUnit otherwise.

若沐 2024-09-12 03:30:47

方法 用 @BeforeTransaction 注解运行,顾名思义,在每个测试的事务开始之前运行。如果在这种方法中你可以检测到测试数据是否加载,那么就可以在需要的时候加载数据。

但请注意,数据将保留在(内存中)数据库中以供所有后续测试使用。

我们使用它来加载“静态”数据,这些数据在生产环境中也会在启动时引导到我们的数据库中。这样,我们实际上使用完全相同的代码和数据进行测试,而不是依赖可能过时的 (DbUnit) 导出。

Methods annotated with @BeforeTransaction run, like its name suggests, before the transaction of each test is started. If in such method you can detect if the test data is loaded, then one can load the data when needed.

Beware though that the data is left in your (in-memory) database for all subsequent tests.

We use this to load "static" data that would, in a production environment, also be bootstrapped into our database when starting it. This way we actually use exactly the same code and data for our tests, rather than relying on (DbUnit) exports that might become outdated.

鹿! 2024-09-12 03:30:47

您可以创建一个数据初始化程序“bean”,因为该配置仅运行一次。它遵循与主要答案相同的原则,但代码和类较少

@Configuration
class DBUnitTest_Config {
  protected String PATH = "";

  @Bean
  public DataSetConfig setupData(DataSource dataSource) throws SQLException {
    DataSetExecutorImpl executor = DataSetExecutorImpl.instance(new ConnectionHolderImpl(dataSource.getConnection()));
    DataSetConfig dataSetConfig = new DataSetConfig(PATH);
    executor.createDataSet(dataSetConfig);
    return dataSetConfig;
  }

}

You can create a data initializer "bean", since the config is only run once. It follows the same principle as the main answer, but with less code and classes

@Configuration
class DBUnitTest_Config {
  protected String PATH = "";

  @Bean
  public DataSetConfig setupData(DataSource dataSource) throws SQLException {
    DataSetExecutorImpl executor = DataSetExecutorImpl.instance(new ConnectionHolderImpl(dataSource.getConnection()));
    DataSetConfig dataSetConfig = new DataSetConfig(PATH);
    executor.createDataSet(dataSetConfig);
    return dataSetConfig;
  }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文