【问题标题】:How to load DBUnit test data once per case with Spring Test如何使用 Spring Test 为每个案例加载一次 DBUnit 测试数据
【发布时间】:2011-03-01 17:57:55
【问题描述】:

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

但是,如果您使用 @BeforeClass Junit 注释,那么这会强制数据加载器是静态的。这里探讨的一个问题:Why must jUnit's fixtureSetup be static?

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

【问题讨论】:

    标签: junit dbunit spring-test


    【解决方案1】:

    一种可行的方法是创建一个“数据初始化器”类,将其添加到一个也有您的数据源的测试 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 的方法,以在整个测试运行结束时执行任何必要的回滚。

    【讨论】:

    • 事实上,这正是我解决问题的方法,我只是忘记将我的代码/答案放回 SO。感谢您花时间为下一个人做这件事。
    • 听起来不错,但实际上另一个类的“@PostConstruct”在 DatabaseInitialiser 之前运行,所以没有用。感谢您的帮助。
    • @united-expression:你能扩展一下吗?你指的是其他哪个班?在我的示例中,Spring 在测试类之前初始化数据库初始化程序,这在问题中概述的 Cass 中起作用。
    • 我有另一个由 Spring 管理的由“@Component”注释的类,它的“@PostConstruct”方法访问数据库,以便在数据库初始化程序之前初始化类。目前的解决方法是为其他类使用自定义注释,但更喜欢更好的解决方案。谢谢。
    • 我喜欢你的方法,我更进一步。检查下面...
    【解决方案2】:

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

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

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

    【讨论】:

    • 这是不好的做法。您如何看待已检查的解决方案?
    【解决方案3】:

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

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

    【讨论】:

    • 我发现 DBUnit 从 XML 加载数据库表的能力很方便。 Spring Test 有这样的能力吗?
    • 是的,我认为 Mats 在这里错了。从 XML 文件加载测试数据对于使用 Spring 进行 db 测试是一个很好的好处。
    【解决方案4】:

    方法annotated with @BeforeTransaction 就像它的名字所暗示的那样,在每个测试的事务开始之前运行。如果通过这种方法可以检测是否加载了测试数据,则可以在需要时加载数据。

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

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

    【讨论】:

      【解决方案5】:

      您可以创建一个数据初始化程序“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;
        }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-19
        • 1970-01-01
        • 2012-01-05
        • 2023-03-02
        • 2015-10-21
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        相关资源
        最近更新 更多