荣耀手机商城官方网站下载,肥东县建设局网站,android编程开发,网页设计公司兴田德润实力强如果我们正在为使用Spring Framework的应用程序编写集成测试#xff0c;则可以通过使用Spring Test DbUnit将DbUnit与Spring测试框架集成。 但是#xff0c; 这种集成并非没有问题 。 通常#xff0c;我们必须在运行测试之前向数据库中插入空值#xff0c;或者验证保存到… 如果我们正在为使用Spring Framework的应用程序编写集成测试则可以通过使用Spring Test DbUnit将DbUnit与Spring测试框架集成。 但是 这种集成并非没有问题 。 通常我们必须在运行测试之前向数据库中插入空值或者验证保存到特定表列中的值是否为空 。 这些是非常基本的用例但是编写支持它们的集成测试非常棘手。 这篇博客文章指出了与null值有关的问题并描述了如何解决它们。 让我们从快速查看被测系统开始。 如果您不知道如何为存储库编写集成测试则应阅读我的博客文章标题为 Spring Data JPA教程集成测试 。 它解释了如何为Spring Data JPA存储库编写集成测试但是可以使用相同的方法为其他使用关系数据库的Spring支持的存储库编写测试。 被测系统 经过测试的“应用程序”具有一个实体和一个Spring Data JPA存储库该存储库为该实体提供CRUD操作。 我们的实体类称为Todo 其源代码的相关部分如下所示 import javax.persistence.*;Entity
Table(nametodos)
public class Todo {private static final int MAX_LENGTH_DESCRIPTION 500;private static final int MAX_LENGTH_TITLE 100;IdGeneratedValue(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类的完整源代码 。 另外我们不应该使用构建器模式因为在创建新的Todo对象时我们的实体只有两个String字段被设置。 但是我在这里使用它是因为它使我们的测试更易于阅读。 我们的Spring Data JPA存储库接口称为TodoRepository 它扩展了CrudRepository TID扩展了Serializable接口。 该存储库为Todo对象提供CRUD操作。 它还声明一种查询方法该方法返回其说明与给定搜索词匹配的所有待办事项条目。 TodoRepository接口的源代码如下所示 import org.springframework.data.repository.CrudRepository;public interface TodoRepository extends CrudRepositoryTodo, Long {ListTodo findByDescription(String description);
} 补充阅读 CrudRepository接口的Javadoc Spring Data JPA教程 Spring Data JPA –参考文档 让我们继续前进了解在编写用于从关系数据库读取信息或将信息保存到其中的代码的集成测试时如何处理空值。 处理空值 在为数据访问代码编写集成测试时 我们必须在每个测试用例之前将数据库初始化为已知状态并确保将正确的数据写入数据库。 本节确定了在编写集成测试以解决我们遇到的问题 使用平面XML数据集。 将空值写入数据库或确保表列的值为null 。 我们还将学习如何解决这些问题。 将空值插入数据库 当我们编写从数据库读取信息的集成测试时必须在调用测试之前将该数据库初始化为已知状态有时我们必须向数据库中插入空值。 因为我们使用平面XML数据集所以可以通过省略相应的属性值将空值插入到表列中。 这意味着如果我们想在todos表的description列中插入null值则可以通过使用以下DbUnit数据集来做到这一点 datasettodos id1 titleFooBar version0/
/dataset 但是通常我们必须在已使用的数据库表中插入多行。 以下DbUnit数据集 todo-entries.xml 将两行插入todos表 datasettodos id1 titleFooBar version0/todos id2 descriptiondescription titletitle version0/
/dataset 让我们找出对TodoRepository接口的findByDescription方法进行集成测试并使用先前的数据集 todo-entries.xml 初始化数据库时发生的情况。 我们的集成测试的源代码如下所示 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;TestDatabaseSetup(todo-entries.xml)public void findByDescription_ShouldReturnOneTodoEntry() {ListTodo 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);}
} 当运行此集成测试时会出现以下断言错误 java.lang.AssertionError:
Expected size:1 but was:0 in: [] 这意味着从数据库中找不到正确的待办事项条目。 发生了什么 我们的查询方法是如此简单以至于它应该起作用特别是因为在调用测试用例之前我们已将正确的数据插入数据库。 好吧实际上两行的描述列都是空的。 DbUnit常见问题说明了发生这种情况的原因 DbUnit使用表的第一个标记来定义要填充的列。 如果此表的以下记录包含额外的列那么将不会填充这些列。 它还提供了解决此问题的方法 从DBUnit 2.3.0开始有一种称为“列检测”的功能该功能基本上将整个XML读入缓冲区并在出现新列时动态添加它们。 我们可以通过反转todos元素的顺序来解决此问题但这很麻烦因为每次创建新数据集时我们都必须记住要做的事情。 我们应该使用列检测因为它消除了人为错误的可能性。 我们可以按照以下步骤启用列检测 创建一个扩展AbstractDataSetLoader类的数据集加载器类。 重写AbstractDataSetLoader类的受保护的IDateSet createDataSetResource resource方法。 通过启用列感应并返回新的FlatXmlDataSet对象来实现此方法。 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);}}
} 补充阅读 FlatXmlDataSet类的Javadoc 现在我们可以通过使用DbUnitConfiguration批注注释测试类并将其加载器属性的值设置为ColumnSensingFlatXmlDataSetLoader.class来配置测试类以使用此数据加载器 。 我们的固定集成测试的源代码如下所示 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;TestDatabaseSetup(todo-entries.xml)public void findByDescription_ShouldReturnOneTodoEntry() {ListTodo 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);}
} 当我们第二次运行集成测试时它通过了。 让我们找出如何验证空值是否已保存到数据库中。 验证表列的值是否为空 在编写将信息保存到数据库的集成测试时我们必须确保将正确的信息确实保存到数据库中有时我们必须验证表列的值为null 。 例如如果我们写这证实了当我们创建一个没有描述一个待办事项条目正确的信息保存到数据库中的集成测试我们必须确保一个空值插入到待办事项表的说明列。 我们的集成测试的源代码如下所示 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;TestDatabaseSetup(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数据集 no-todo-entries.xml 如下所示 datasettodos/
/dataset 因为我们没有设置保存的todo条目的描述 所以todos表的description列应该为null 。 这意味着我们应该从数据集中省略它以验证是否将正确的信息保存到数据库中。 该数据集 save-todo-entry-without-description-expected.xml 如下所示 datasettodos id1 titletitle version0/
/dataset 当我们运行集成测试时它失败并且我们看到以下错误消息 junit.framework.ComparisonFailure: column count (tabletodos, expectedColCount3, actualColCount4)
Expected :[id, title, version]
Actual :[DESCRIPTION, ID, TITLE, VERSION] 问题在于DbUnit期望todos表仅具有id title和version列。 这样做的原因是这些列是从数据集的第一行也是唯一的行中找到的唯一列。 我们可以使用ReplacementDataSet解决此问题。 ReplacementDataSet是一个装饰器它用替换对象替换从平面XML数据集文件中找到的占位符。 让我们修改自定义的数据集加载类返回一个ReplacementDataSet对象替换“[空]”字符串与空 。 我们可以通过对自定义数据集加载器进行以下更改来做到这一点 将私有的createReplacementDataSet方法添加到数据集加载器类。 此方法返回一个ReplacementDataSet对象并将FlatXmlDataSet对象作为方法参数。 通过创建新的ReplacementDataSet对象并返回创建的对象来实现此方法。 修改createDataSet方法以调用私有的createReplacementDataSet方法并返回创建的ReplacementDataSet对象。 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;}
} 补充阅读 IDataSet接口的最常用实现 ReplacementDataSet类的Javadoc 我们可以按照以下步骤修复集成测试 通过使用ColumnSensingReplacementDataSetLoader类配置我们的测试类以加载使用的DbUnit数据集。 修改我们的数据集以验证description列的值为null 。 首先 我们必须配置测试类以使用ColumnSensingReplacementDataSetLoader类加载DbUnit数据集。 因为我们已经使用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;TestDatabaseSetup(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);}
} 其次 我们必须验证是否将空值保存到todos表的description列中。 为此我们可以向数据集中唯一的todos元素添加一个description属性并将description属性的值设置为[null]。 我们的固定数据集 save-todo-entry-without-description-expected.xml 如下所示 datasettodos id1 description[null] titletitle version0/
/dataset 当我们运行集成测试时它会通过。 让我们继续并总结从这篇博客文章中学到的知识。 摘要 这篇博客文章教会了我们四件事 DbUnit假定数据库表仅包含从指定表行的列的第一个标记中找到的那些列。 如果要覆盖此行为则必须启用DbUnit的列感测功能。 如果要确保将null值保存到数据库则必须使用替换数据集。 我们了解了如何创建自定义数据集加载器该加载器创建替换数据集并使用列感测。 我们了解了如何配置用于加载DbUnit数据集的数据集加载器。 您可以从Github获得此博客文章的示例应用程序 。 翻译自: https://www.javacodegeeks.com/2014/11/spring-from-the-trenches-using-null-values-in-dbunit-datasets.html