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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

摆脱困境:在DbUnit数据集中使用空值

發(fā)布時(shí)間:2023/12/3 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 摆脱困境:在DbUnit数据集中使用空值 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

如果我們正在為使用Spring Framework的應(yīng)用程序編寫集成測試,則可以通過使用Spring Test DbUnit將DbUnit與Spring測試框架集成。

但是, 這種集成并非沒有問題

通常,我們必須在運(yùn)行測試之前向數(shù)據(jù)庫中插入空值,或者驗(yàn)證保存到特定表列中的值是否為空 。 這些是非常基本的用例,但是編寫支持它們的集成測試非常棘手。

這篇博客文章指出了與null值有關(guān)的問題,并描述了如何解決它們。 讓我們從快速查看被測系統(tǒng)開始。

如果您不知道如何為存儲(chǔ)庫編寫集成測試,則應(yīng)閱讀我的博客文章,標(biāo)題為: Spring Data JPA教程:集成測試 。

它解釋了如何為Spring Data JPA存儲(chǔ)庫編寫集成測試,但是可以使用相同的方法為其他使用關(guān)系數(shù)據(jù)庫的Spring支持的存儲(chǔ)庫編寫測試。

被測系統(tǒng)

經(jīng)過測試的“應(yīng)用程序”具有一個(gè)實(shí)體和一個(gè)Spring Data JPA存儲(chǔ)庫,該存儲(chǔ)庫為該實(shí)體提供CRUD操作。

我們的實(shí)體類稱為Todo ,其源代碼的相關(guān)部分如下所示:

import javax.persistence.*;@Entity @Table(name="todos") public class Todo {private static final int MAX_LENGTH_DESCRIPTION = 500;private static final int MAX_LENGTH_TITLE = 100;@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "description", nullable = true, length = MAX_LENGTH_DESCRIPTION)private String description;@Column(name = "title", nullable = false, length = MAX_LENGTH_TITLE)private String title;@Versionprivate long version;//Constructors, builder class, and getters are omitted. }
  • 您可以從Github獲取Todo類的完整源代碼 。

另外,我們不應(yīng)該使用構(gòu)建器模式,因?yàn)樵趧?chuàng)建新的Todo對(duì)象時(shí),我們的實(shí)體只有兩個(gè)String字段被設(shè)置。 但是,我在這里使用它是因?yàn)樗刮覀兊臏y試更易于閱讀。

我們的Spring Data JPA存儲(chǔ)庫接口稱為TodoRepository ,它擴(kuò)展了CrudRepository <T,ID擴(kuò)展了Serializable>接口。 該存儲(chǔ)庫為Todo對(duì)象提供CRUD操作。 它還聲明一種查詢方法,該方法返回其說明與給定搜索詞匹配的所有待辦事項(xiàng)條目。

TodoRepository接口的源代碼如下所示:

import org.springframework.data.repository.CrudRepository;public interface TodoRepository extends CrudRepository<Todo, Long> {List<Todo> findByDescription(String description); }

補(bǔ)充閱讀:

  • CrudRepository接口的Javadoc
  • Spring Data JPA教程
  • Spring Data JPA –參考文檔

讓我們繼續(xù)前進(jìn),了解在編寫用于從關(guān)系數(shù)據(jù)庫讀取信息或?qū)⑿畔⒈4娴狡渲械拇a的集成測試時(shí),如何處理空值。

處理空值

在為數(shù)據(jù)訪問代碼編寫集成測試時(shí) ,我們必須在每個(gè)測試用例之前將數(shù)據(jù)庫初始化為已知狀態(tài),并確保將正確的數(shù)據(jù)寫入數(shù)據(jù)庫。

本節(jié)確定了在編寫集成測試以解決我們遇到的問題

  • 使用平面XML數(shù)據(jù)集。
  • 將空值寫入數(shù)據(jù)庫或確保表列的值為null 。

我們還將學(xué)習(xí)如何解決這些問題。

將空值插入數(shù)據(jù)庫

當(dāng)我們編寫從數(shù)據(jù)庫讀取信息的集成測試時(shí),必須在調(diào)用測試之前將該數(shù)據(jù)庫初始化為已知狀態(tài),有時(shí)我們必須向數(shù)據(jù)庫中插入空值。

因?yàn)槲覀兪褂闷矫鎄ML數(shù)據(jù)集,所以可以通過省略相應(yīng)的屬性值將空值插入到表列中。 這意味著,如果我們想在todos表的description列中插入null值,則可以通過使用以下DbUnit數(shù)據(jù)集來做到這一點(diǎn):

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

但是,通常我們必須在已使用的數(shù)據(jù)庫表中插入多行。 以下DbUnit數(shù)據(jù)集( todo-entries.xml )將兩行插入todos表:

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

讓我們找出對(duì)TodoRepository接口的findByDescription()方法進(jìn)行集成測試并使用先前的數(shù)據(jù)集( todo-entries.xml )初始化數(shù)據(jù)庫時(shí)發(fā)生的情況。 我們的集成測試的源代碼如下所示:

import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; 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 static org.assertj.core.api.Assertions.assertThat;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.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("todo-entries.xml")public void findByDescription_ShouldReturnOneTodoEntry() {List<Todo> todoEntries = repository.findByDescription(DESCRIPTION);assertThat(todoEntries).hasSize(1);Todo found = todoEntries.get(0);assertThat(found.getId()).isEqualTo(ID);assertThat(found.getTitle()).isEqualTo(TITLE);assertThat(found.getDescription()).isEqualTo(DESCRIPTION);assertThat(found.getVersion()).isEqualTo(VERSION);} }

當(dāng)運(yùn)行此集成測試時(shí),會(huì)出現(xiàn)以下斷言錯(cuò)誤:

java.lang.AssertionError: Expected size:<1> but was:<0> in: <[]>

這意味著從數(shù)據(jù)庫中找不到正確的待辦事項(xiàng)條目。 發(fā)生了什么? 我們的查詢方法是如此簡單,以至于它應(yīng)該起作用,特別是因?yàn)樵谡{(diào)用測試用例之前,我們已將正確的數(shù)據(jù)插入數(shù)據(jù)庫。

好吧,實(shí)際上兩行的描述列都是空的。 DbUnit常見問題說明了發(fā)生這種情況的原因 :

DbUnit使用表的第一個(gè)標(biāo)記來定義要填充的列。 如果此表的以下記錄包含額外的列,那么將不會(huì)填充這些列。

它還提供了解決此問題的方法:

從DBUnit 2.3.0開始,有一種稱為“列檢測”的功能,該功能基本上將整個(gè)XML讀入緩沖區(qū),并在出現(xiàn)新列時(shí)動(dòng)態(tài)添加它們。

我們可以通過反轉(zhuǎn)todos元素的順序來解決此問題,但這很麻煩,因?yàn)槊看蝿?chuàng)建新數(shù)據(jù)集時(shí)我們都必須記住要做的事情。 我們應(yīng)該使用列檢測,因?yàn)樗巳藶殄e(cuò)誤的可能性。

我們可以按照以下步驟啟用列檢測:

  • 創(chuàng)建一個(gè)擴(kuò)展AbstractDataSetLoader類的數(shù)據(jù)集加載器類。
  • 重寫AbstractDataSetLoader類的受保護(hù)的IDateSet createDataSet(Resource resource)方法。
  • 通過啟用列感應(yīng)并返回新的FlatXmlDataSet對(duì)象來實(shí)現(xiàn)此方法。
  • ColumnSensingFlatXmlDataSetLoader類的源代碼如下所示:

    import com.github.springtestdbunit.dataset.AbstractDataSetLoader; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.springframework.core.io.Resource; import java.io.InputStream;public class ColumnSensingFlatXMLDataSetLoader extends AbstractDataSetLoader {@Overrideprotected IDataSet createDataSet(Resource resource) throws Exception {FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();builder.setColumnSensing(true);try (InputStream inputStream = resource.getInputStream()) {return builder.build(inputStream);}} }

    補(bǔ)充閱讀:

    • FlatXmlDataSet類的Javadoc

    現(xiàn)在,我們可以通過使用@DbUnitConfiguration批注注釋測試類并將其加載器屬性的值設(shè)置為ColumnSensingFlatXmlDataSetLoader.class,來配置測試類以使用此數(shù)據(jù)加載器 。

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

    import com.github.springtestdbunit.DbUnitTestExecutionListener; import com.github.springtestdbunit.annotation.DatabaseSetup; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; 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 static org.assertj.core.api.Assertions.assertThat;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.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("todo-entries.xml")public void findByDescription_ShouldReturnOneTodoEntry() {List<Todo> todoEntries = repository.findByDescription(DESCRIPTION);assertThat(todoEntries).hasSize(1);Todo found = todoEntries.get(0);assertThat(found.getId()).isEqualTo(ID);assertThat(found.getTitle()).isEqualTo(TITLE);assertThat(found.getDescription()).isEqualTo(DESCRIPTION);assertThat(found.getVersion()).isEqualTo(VERSION);} }

    當(dāng)我們第二次運(yùn)行集成測試時(shí),它通過了。

    讓我們找出如何驗(yàn)證空值是否已保存到數(shù)據(jù)庫中。

    驗(yàn)證表列的值是否為空

    在編寫將信息保存到數(shù)據(jù)庫的集成測試時(shí),我們必須確保將正確的信息確實(shí)保存到數(shù)據(jù)庫中,有時(shí)我們必須驗(yàn)證表列的值為null 。

    例如,如果我們寫這證實(shí)了,當(dāng)我們創(chuàng)建一個(gè)沒有描述一個(gè)待辦事項(xiàng)條目正確的信息保存到數(shù)據(jù)庫中的集成測試,我們必須確保一個(gè)空值插入到待辦事項(xiàng)表的說明列。

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

    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.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 static org.assertj.core.api.Assertions.assertThat;@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {PersistenceContext.class}) @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,DirtiesContextTestExecutionListener.class,TransactionalTestExecutionListener.class,DbUnitTestExecutionListener.class }) @DbUnitConfiguration(dataSetLoader = ColumnSensingFlatXMLDataSetLoader.class) public class ITTodoRepositoryTest {private static final String DESCRIPTION = "description";private static final String TITLE = "title";@Autowiredprivate TodoRepository repository;@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);} }

    這不是一個(gè)很好的集成測試,因?yàn)樗鼉H測試Spring Data JPA和Hibernate是否正常工作。 我們不應(yīng)該通過為框架編寫測試來浪費(fèi)時(shí)間。 如果我們不信任框架,則不應(yīng)使用它。

    如果您想學(xué)習(xí)為數(shù)據(jù)訪問代碼編寫好的集成測試,則應(yīng)該閱讀我的教程: 編寫數(shù)據(jù)訪問代碼的測試 。

    用于初始化數(shù)據(jù)庫的DbUnit數(shù)據(jù)集( no-todo-entries.xml )如下所示:

    <dataset><todos/> </dataset>

    因?yàn)槲覀儧]有設(shè)置保存的todo條目的描述 ,所以todos表的description列應(yīng)該為null 。 這意味著我們應(yīng)該從數(shù)據(jù)集中省略它,以驗(yàn)證是否將正確的信息保存到數(shù)據(jù)庫中。

    該數(shù)據(jù)集( save-todo-entry-without-description-expected.xml )如下所示:

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

    當(dāng)我們運(yùn)行集成測試時(shí),它失敗,并且我們看到以下錯(cuò)誤消息:

    junit.framework.ComparisonFailure: column count (table=todos, expectedColCount=3, actualColCount=4) Expected :[id, title, version] Actual :[DESCRIPTION, ID, TITLE, VERSION]

    問題在于DbUnit期望todos表僅具有id , title和version列。 這樣做的原因是,這些列是從數(shù)據(jù)集的第一行(也是唯一的行)中找到的唯一列。

    我們可以使用ReplacementDataSet解決此問題。 ReplacementDataSet是一個(gè)裝飾器,它用替換對(duì)象替換從平面XML數(shù)據(jù)集文件中找到的占位符。 讓我們修改自定義的數(shù)據(jù)集加載類返回一個(gè)ReplacementDataSet對(duì)象替換“[空]”字符串與空 。

    我們可以通過對(duì)自定義數(shù)據(jù)集加載器進(jìn)行以下更改來做到這一點(diǎn):

  • 將私有的createReplacementDataSet()方法添加到數(shù)據(jù)集加載器類。 此方法返回一個(gè)ReplacementDataSet對(duì)象,并將FlatXmlDataSet對(duì)象作為方法參數(shù)。
  • 通過創(chuàng)建新的ReplacementDataSet對(duì)象并返回創(chuàng)建的對(duì)象來實(shí)現(xiàn)此方法。
  • 修改createDataSet()方法以調(diào)用私有的createReplacementDataSet()方法并返回創(chuàng)建的ReplacementDataSet對(duì)象。
  • ColumnSensingReplacementDataSetLoader類的源代碼如下所示:

    import com.github.springtestdbunit.dataset.AbstractDataSetLoader; import org.dbunit.dataset.IDataSet; import org.dbunit.dataset.ReplacementDataSet; import org.dbunit.dataset.xml.FlatXmlDataSet; import org.dbunit.dataset.xml.FlatXmlDataSetBuilder; import org.springframework.core.io.Resource;import java.io.InputStream;public class ColumnSensingReplacementDataSetLoader extends AbstractDataSetLoader {@Overrideprotected IDataSet createDataSet(Resource resource) throws Exception {FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();builder.setColumnSensing(true);try (InputStream inputStream = resource.getInputStream()) {return createReplacementDataSet(builder.build(inputStream));}}private ReplacementDataSet createReplacementDataSet(FlatXmlDataSet dataSet) {ReplacementDataSet replacementDataSet = new ReplacementDataSet(dataSet);//Configure the replacement dataset to replace '[null]' strings with null.replacementDataSet.addReplacementObject("[null]", null);return replacementDataSet;} }

    補(bǔ)充閱讀:

    • IDataSet接口的最常用實(shí)現(xiàn)
    • ReplacementDataSet類的Javadoc

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

  • 通過使用ColumnSensingReplacementDataSetLoader類,配置我們的測試類以加載使用的DbUnit數(shù)據(jù)集。
  • 修改我們的數(shù)據(jù)集以驗(yàn)證description列的值為null 。
  • 首先 ,我們必須配置測試類以使用ColumnSensingReplacementDataSetLoader類加載DbUnit數(shù)據(jù)集。 因?yàn)槲覀円呀?jīng)使用@DbUnitConfiguration為測試類添加了注釋 ,所以我們必須將其loader屬性的值更改為ColumnSensingReplacementDataSetLoader.class 。

    固定測試類的源代碼如下所示:

    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.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 static org.assertj.core.api.Assertions.assertThat;@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 String DESCRIPTION = "description";private static final String TITLE = "title";@Autowiredprivate TodoRepository repository;@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);} }

    其次 ,我們必須驗(yàn)證是否將空值保存到todos表的description列中。 為此,我們可以向數(shù)據(jù)集中唯一的todos元素添加一個(gè)description屬性,并將description屬性的值設(shè)置為'[null]'。

    我們的固定數(shù)據(jù)集( save-todo-entry-without-description-expected.xml )如下所示:

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

    當(dāng)我們運(yùn)行集成測試時(shí),它會(huì)通過。

    讓我們繼續(xù)并總結(jié)從這篇博客文章中學(xué)到的知識(shí)。

    摘要

    這篇博客文章教會(huì)了我們四件事:

    • DbUnit假定數(shù)據(jù)庫表僅包含從指定表行的列的第一個(gè)標(biāo)記中找到的那些列。 如果要覆蓋此行為,則必須啟用DbUnit的列感測功能。
    • 如果要確保將null值保存到數(shù)據(jù)庫,則必須使用替換數(shù)據(jù)集。
    • 我們了解了如何創(chuàng)建自定義數(shù)據(jù)集加載器,該加載器創(chuàng)建替換數(shù)據(jù)集并使用列感測。
    • 我們了解了如何配置用于加載DbUnit數(shù)據(jù)集的數(shù)據(jù)集加載器。

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

    翻譯自: https://www.javacodegeeks.com/2014/11/spring-from-the-trenches-using-null-values-in-dbunit-datasets.html

    總結(jié)

    以上是生活随笔為你收集整理的摆脱困境:在DbUnit数据集中使用空值的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。