网站建设合同要上印花税吗,长沙建网站一般多少钱,手机app商城定制公司,做网站租用服务器mysql重置增量当我们为将信息保存到数据库的功能编写集成测试时#xff0c;我们必须验证是否将正确的信息保存到数据库。 如果我们的应用程序使用Spring Framework#xff0c;则可以为此目的使用Spring Test DbUnit和DbUnit 。 但是#xff0c;很难验证是否在主键列中插入… mysql重置增量 当我们为将信息保存到数据库的功能编写集成测试时我们必须验证是否将正确的信息保存到数据库。 如果我们的应用程序使用Spring Framework则可以为此目的使用Spring Test DbUnit和DbUnit 。 但是很难验证是否在主键列中插入了正确的值因为主键通常是通过使用自动增量或序列自动生成的。 这篇博客文章指出了与自动生成值的列相关的问题并帮助我们解决了这一问题。 补充阅读 经测试的应用程序在博客文章中进行了描述该博客文章的标题为 摆脱困境在DbUnit数据集中使用空值 。 我建议您阅读该博客文章因为我不会在此博客文章上重复其内容。 如果您不知道如何为存储库编写集成测试则应阅读我的博客文章标题为 Spring Data JPA教程集成测试 。 它说明了如何为Spring Data JPA存储库编写集成测试但是您可以使用与为使用关系数据库的其他Spring Powered存储库编写测试的方法相同的方法。 我们无法断言未知 让我们从为CrudRepository接口的save方法编写两个集成测试开始。 这些测试描述如下 第一个测试确保在设置了保存的Todo对象的标题和描述时将正确的信息保存到数据库中。 当仅设置了已保存的Todo对象的标题时第二个测试将验证是否将正确的信息保存到数据库中。 这两个测试都通过使用相同的DbUnit数据集 no-todo-entries.xml 来初始化使用的数据库该数据集如下所示 datasettodos/
/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;TestDatabaseSetup(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);}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数据集 save-todo-entry-with-title-and-description-expected.xml 用于验证将已保存的Todo对象的标题和描述插入到todos表中如下所示 datasettodos id1 descriptiondescription titletitle version0/
/dataset DbUnit数据集 save-todo-entry-without-description-expected.xml 用于验证是否仅将已保存的Todo对象的标题插入了todos表如下所示 datasettodos id1 description[null] titletitle version0/
/dataset 当我们运行集成测试时其中之一失败并且我们看到以下错误消息 junit.framework.ComparisonFailure: value (tabletodos, row0, colid)
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不是解决此特定问题的正确工具因为它迫使我们编写用于验证很少内容的数据集。 例如我们不能使用以下数据集 datasettodos id1 descriptiondescription titletitle version0/todos descriptiondescription two titletitle two version0/
/dataset 如果我们使用DatabaseAssertionMode.NON_STRICT 则数据集的每个“行”必须指定相同的列。 换句话说我们必须将数据集修改为如下所示 datasettodos descriptiondescription titletitle version0/todos descriptiondescription two titletitle two version0/
/dataset 没什么大不了的因为我们可以相信Hibernate将正确的id插入todos表的id列中。 但是如果每个待办事项条目都可以包含0 .. *标签那么我们将会遇到麻烦。 假设我们必须编写一个集成测试该测试将两个新的todo条目插入数据库并创建一个DbUnit数据集以确保 标题为“ title one”的待办事项条目具有名为“ tag one”的标签。 标题为“标题二”的待办事项条目具有名为“标签二”的标签。 我们的最大努力如下所示 datasettodos descriptiondescription titletitle one version0/todos descriptiondescription two titletitle two version0/tags nametag one version0/tags nametag two version0/
/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.templateALTER 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);}TestDatabaseSetup(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);}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);}
} 附加信息 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.htmlmysql重置增量