日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

摆脱困境:在每种测试方法之前重置自动增量列

發布時間:2023/12/3 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 摆脱困境:在每种测试方法之前重置自动增量列 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

當我們為將信息保存到數據庫的功能編寫集成測試時,我們必須驗證是否將正確的信息保存到數據庫。

如果我們的應用程序使用Spring Framework,則可以為此目的使用Spring Test DbUnit和DbUnit 。

但是,很難驗證是否在主鍵列中插入了正確的值,因為主鍵通常是通過使用自動增量或序列自動生成的。

這篇博客文章指出了與自動生成值的列相關的問題,并幫助我們解決了這一問題。

補充閱讀:

  • 在標題為“ 從溝壑中彈跳:在DbUnit數據集中使用空值 ”的博客文章中描述了經過測試的應用程序。 我建議您閱讀該博客文章,因為我不會在此博客文章上重復其內容。
  • 如果您不知道如何為存儲庫編寫集成測試,則應閱讀我的博客文章,標題為: Spring Data JPA教程:集成測試 。 它說明了如何為Spring Data JPA存儲庫編寫集成測試,但是您可以使用與為使用關系數據庫的其他Spring Powered存儲庫編寫測試的方法相同的方法。

我們無法斷言未知

讓我們開始為CrudRepository接口的save()方法編寫兩個集成測試。 這些測試描述如下:

  • 第一個測試確保在設置了保存的Todo對象的標題和描述時將正確的信息保存到數據庫中。
  • 當僅設置了保存的Todo對象的標題時,第二個測試將驗證是否將正確的信息保存到數據庫中。

這兩個測試都通過使用相同的DbUnit數據集( no-todo-entries.xml )初始化使用的數據庫,該數據集如下所示:

<dataset><todos/> </dataset>

我們的集成測試類的源代碼如下所示:

import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingReplacementDataSetLoader.class) public class ITTodoRepositoryTest {private static final Long ID = 2L;private static final String DESCRIPTION = "description";private static final String TITLE = "title";private static final long VERSION = 0L;@Autowiredprivate TodoRepository repository;@Test@DatabaseSetup("no-todo-entries.xml")@ExpectedDatabase("save-todo-entry-with-title-and-description-expected.xml")public void save_WithTitleAndDescription_ShouldSaveTodoEntryToDatabase() {Todo todoEntry = Todo.getBuilder().title(TITLE).description(DESCRIPTION).build();repository.save(todoEntry);}@Test@DatabaseSetup("no-todo-entries.xml")@ExpectedDatabase("save-todo-entry-without-description-expected.xml")public void save_WithoutDescription_ShouldSaveTodoEntryToDatabase() {Todo todoEntry = Todo.getBuilder().title(TITLE).description(null).build();repository.save(todoEntry);} }

這些不是很好的集成測試,因為它們僅測試Spring Data JPA和Hibernate是否正常工作。 我們不應該通過為框架編寫測試來浪費時間。 如果我們不信任框架,則不應使用它。

如果您想學習為數據訪問代碼編寫好的集成測試,則應該閱讀我的教程: 編寫數據訪問代碼的測試 。

DbUnit數據集( save-todo-entry-with-title-and-description-expected.xml )用于驗證已保存的Todo對象的標題和描述是否已插入todos表中,如下所示:

<dataset><todos id="1" description="description" title="title" version="0"/> </dataset>

DbUnit數據集( save-todo-entry-without-description-expected.xml )用于驗證是否僅將已保存的Todo對象的標題插入了todos表,如下所示:

<dataset><todos id="1" description="[null]" title="title" version="0"/> </dataset>

當我們運行集成測試時,其中之一失敗,并且我們看到以下錯誤消息:

junit.framework.ComparisonFailure: value (table=todos, row=0, col=id) Expected :1 Actual :2

原因是todos表的id列是一個自動增量列,并且首先調用的集成測試“獲取”了id1。在調用第二個集成測試時,值2保存到了id列,測試失敗。

讓我們找出如何解決這個問題。

快速修復贏?

有兩個快速解決我們的問題的方法。 這些修復程序描述如下:

首先 ,我們可以使用@DirtiesContext批注注釋測試類,并將其classMode屬性的值設置為DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD。這將解決我們的問題,因為在加載應用程序上下文時,我們的應用程序會創建一個新的內存數據庫,并且@DirtiesContext注釋可確保每個測試方法都使用新的應用程序上下文。

我們的測試類的配置如下所示:

import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingReplacementDataSetLoader.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) public class ITTodoRepositoryTest {}

這看起來很干凈,但不幸的是,它可能會破壞我們的集成測試套件的性能,因為它會在調用每個測試方法之前創建一個新的應用程序上下文。 這就是為什么我們不應該使用@DirtiesContext批注,除非它是絕對必要的

但是,如果我們的應用程序只有少量的集成測試,則@DirtiesContext批注引起的性能損失可能是可以容忍的。 我們不應該僅僅因為它會使我們的測試變慢而放棄該解決方案。 有時這是可以接受的,并且在這種情況下,使用@DirtiesContext注釋是一個很好的解決方案。

補充閱讀:

  • @DirtiesContext注釋的Javadoc
  • @ DirtiesContext.ClassMode枚舉的Javadoc

其次 ,我們可以從數據集中省略todos元素的id屬性,并將@ExpectedDatabase批注的assertionMode屬性的值設置為DatabaseAssertionMode.NON_STRICT 。 這將解決我們的問題,因為DatabaseAssertionMode.NON_STRICT意味著將忽略數據集文件中不存在的列和表。

該斷言模式是一個有用的工具,因為它使我們有可能忽略其信息不會被測試代碼更改的表。 但是, DatabaseAssertionMode.NON_STRICT不是解決此特定問題的正確工具,因為它迫使我們編寫用于驗證很少內容的數據集。

例如,我們不能使用以下數據集:

<dataset><todos id="1" description="description" title="title" version="0"/><todos description="description two" title="title two" version="0"/> </dataset>

如果我們使用DatabaseAssertionMode.NON_STRICT ,則數據集的每個“行”必須指定相同的列。 換句話說,我們必須將數據集修改為如下所示:

<dataset><todos description="description" title="title" version="0"/><todos description="description two" title="title two" version="0"/> </dataset>

沒什么大不了的,因為我們可以相信Hibernate將正確的ID插入todos表的id列中。

但是,如果每個待辦事項條目都可以包含0 .. *標簽,那么我們將會遇到麻煩。 假設我們必須編寫一個集成測試,該測試將兩個新的todo條目插入數據庫并創建一個DbUnit數據集,以確保

  • 標題為“ title one”的待辦事項條目具有一個名為“ tag one”的標簽。
  • 標題為“標題二”的待辦事項條目具有名為“標簽二”的標簽。

我們的最大努力如下所示:

<dataset><todos description="description" title="title one" version="0"/><todos description="description two" title="title two" version="0"/><tags name="tag one" version="0"/><tags name="tag two" version="0"/> </dataset>

我們無法創建有用的DbUnit數據集,因為我們不知道保存到數據庫中的待辦事項條目的ID。

我們必須找到更好的解決方案。

尋找更好的解決方案

我們已經為我們的問題找到了兩種不同的解決方案,但是它們都產生了新的問題。 第三種解決方案基于以下思想:

如果我們不知道插入到自動增量列中的下一個值,則必須在調用每個測試方法之前重置自動增量列。

我們可以按照以下步驟進行操作:

  • 創建一個用于重置指定數據庫表的自動增量列的類。
  • 修復我們的集成測試。
  • 讓我們弄臟雙手。

    創建可以重置自動增量列的類

    我們可以通過執行以下步驟來創建該類,該類可以重置指定數據庫表的自動增量列:

  • 創建一個名為DbTestUtil的最終類,并通過向其添加私有構造函數來防止其實例化。
  • 將公共靜態void resetAutoIncrementColumns()方法添加到DbTestUtil類。 此方法采用兩個方法參數:
  • ApplicationContext對象包含測試的應用程序的配置。
  • 必須重置其自動增量列的數據庫表的名稱。
  • 通過執行以下步驟來實現此方法:
  • 獲取對DataSource對象的引用。
  • 通過使用鍵“ test.reset.sql.template”從屬性文件( application.properties )中讀取SQL模板。
  • 打開數據庫連接。
  • 創建調用的SQL語句并調用它們。
  • DbTestUtil類的源代碼如下所示:

    import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement;public final class DbTestUtil {private DbTestUtil() {}public static void resetAutoIncrementColumns(ApplicationContext applicationContext,String... tableNames) throws SQLException {DataSource dataSource = applicationContext.getBean(DataSource.class);String resetSqlTemplate = getResetSqlTemplate(applicationContext);try (Connection dbConnection = dataSource.getConnection()) {//Create SQL statements that reset the auto increment columns and invoke //the created SQL statements.for (String resetSqlArgument: tableNames) {try (Statement statement = dbConnection.createStatement()) {String resetSql = String.format(resetSqlTemplate, resetSqlArgument);statement.execute(resetSql);}}}}private static String getResetSqlTemplate(ApplicationContext applicationContext) {//Read the SQL template from the properties fileEnvironment environment = applicationContext.getBean(Environment.class);return environment.getRequiredProperty("test.reset.sql.template");} }

    附加信息:

    • ApplicationContext接口的Javadoc
    • DataSource接口的Javadoc
    • 環境接口的Javadoc
    • String.format()方法的Javadoc

    讓我們繼續前進,找出如何在集成測試中使用此類。

    修復我們的集成測試

    我們可以按照以下步驟修復集成測試:

  • 將重置的SQL模板添加到示例應用程序的屬性文件中。
  • 在調用我們的測試方法之前,重置todos表的自動增量列( id )。
  • 首先 ,我們必須將重置的SQL模板添加到示例應用程序的屬性文件中。 此模板必須使用String類的format()方法支持的格式 。 因為我們的示例應用程序使用H2內存數據庫,所以我們必須將以下SQL模板添加到屬性文件中:

    test.reset.sql.template=ALTER TABLE %s ALTER COLUMN id RESTART WITH 1

    附加信息:

    • 我們的示例應用程序的應用程序上下文配置類
    • String.format()方法的Javadoc
    • 重置H2中的自動增量
    • 如何重置MySQL自動增量列
    • PostgreSQL 9.3文檔:ALTER SEQUENCE

    其次 ,在調用我們的測試方法之前,我們必須重置todos表的自動增量列( id )。 我們可以通過對ITTodoRepositoryTest類進行以下更改來做到這一點:

  • 將包含我們示例應用程序配置的ApplicationContext對象注入到測試類中。
  • 重置待辦事項表的自動增量列。
  • 固定集成測試類的源代碼如下所示(突出顯示了更改):

    import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import com.github.springtestdbunit.annotation.DbUnitConfiguration; import com.github.springtestdbunit.annotation.ExpectedDatabase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; import org.springframework.test.context.transaction.TransactionalTestExecutionListener;import java.sql.SQLException;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingReplacementDataSetLoader.class) public class ITTodoRepositoryTest {private static final Long ID = 2L;private static final String DESCRIPTION = "description";private static final String TITLE = "title";private static final long VERSION = 0L;@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate TodoRepository repository;@Beforepublic void setUp() throws SQLException {DbTestUtil.resetAutoIncrementColumns(applicationContext, "todos");}@Test@DatabaseSetup("no-todo-entries.xml")@ExpectedDatabase("save-todo-entry-with-title-and-description-expected.xml")public void save_WithTitleAndDescription_ShouldSaveTodoEntryToDatabase() {Todo todoEntry = Todo.getBuilder().title(TITLE).description(DESCRIPTION).build();repository.save(todoEntry);}@Test@DatabaseSetup("no-todo-entries.xml")@ExpectedDatabase("save-todo-entry-without-description-expected.xml")public void save_WithoutDescription_ShouldSaveTodoEntryToDatabase() {Todo todoEntry = Todo.getBuilder().title(TITLE).description(null).build();repository.save(todoEntry);} }

    附加信息:

    • @Autowired批注的Javadoc
    • ApplicationContext接口的Javadoc
    • @Before注釋的Javadoc

    當我們第二次運行集成測試時,它們通過了。

    讓我們繼續并總結從這篇博客文章中學到的知識。

    摘要

    這個博客教會了我們三件事:

    • 如果我們不知道插入值自動生成的列中的值,我們將無法編寫有用的集成測試。
    • 如果我們的應用程序沒有很多集成測試,則使用@DirtiesContext注釋可能是一個不錯的選擇。
    • 如果我們的應用程序有很多集成測試,則必須在調用每種測試方法之前重置自動增量列。

    您可以從Github獲得此博客文章的示例應用程序 。

    翻譯自: https://www.javacodegeeks.com/2014/11/spring-from-the-trenches-resetting-auto-increment-columns-before-each-test-method.html

    總結

    以上是生活随笔為你收集整理的摆脱困境:在每种测试方法之前重置自动增量列的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。