使用dbUnit,JSON,HSQLDB和JUnit规则进行数据库单元测试

在本周TDD课程的运行中,我认为编写一些夹具以简化dbUnit的使用将很有趣。 我最初的想法只是教dbUnit有关JSON的知识,但事实证明Lieven Doclo已经做到了。 因此,我决定更进一步,还将dbUnit与JUnit Rules结合起来,并提供HSQLDB内存对象存储的自动引导。

以下测试显示了我最终得到的结果:
package com.danhaywood.tdd.dbunit.test;import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;import java.sql.ResultSet;
import java.sql.Statement;import org.dbunit.Assertion;
import org.dbunit.dataset.ITable;
import org.hsqldb.jdbcDriver;
import org.junit.Rule;
import org.junit.Test;import com.danhaywood.tdd.dbunit.DbUnitRule;
import com.danhaywood.tdd.dbunit.DbUnitRule.Ddl;
import com.danhaywood.tdd.dbunit.DbUnitRule.JsonData;public class DbUnitRuleExample {@Rulepublic DbUnitRule dbUnit = new DbUnitRule(DbUnitRuleExample.class, jdbcDriver.class,"jdbc:hsqldb:file:src/test/resources/testdb", "SA", "");@Ddl("customer.ddl")@JsonData("customer.json")@Testpublic void update_lastName() throws Exception {// whenStatement statement = dbUnit.getConnection().createStatement();statement.executeUpdate("update customer set last_name='Bloggs' where id=2");// then (verify directly)ResultSet rs2 = dbUnit.executeQuery("select last_name from customer where id = 2");assertThat(rs2.next(), is(true));assertThat(rs2.getString("last_name"), equalTo("Bloggs"));// then (verify using datasets)ITable actualTable = dbUnit.createQueryTable("customer", "select * from customer order by id");ITable expectedTable = dbUnit.jsonDataSet("customer-updated.json").getTable("customer");Assertion.assertEquals(expectedTable, actualTable);}
}

其中customer.ddl是:

drop table customer if exists;
create table customer (id         int         not null primary key,first_name varchar(30) not null,initial    varchar(1)  null,last_name  varchar(30) not null
)

customer.json (初始数据集)为:

{"customer":[{"id": 1,"first_name": "John","initial": "K","last_name": "Smith"},{"id": 2,"first_name": "Mary","last_name": "Jones"}]
}

customer-updated.json (最终数据集)为:

{"customer":[{"id": 1,"first_name": "John","initial": "K","last_name": "Smith"},{"id": 2,"first_name": "Mary","last_name": "Bloggs"}]
}

您可能已经发现, @Ddl注释可以选择指定要针对数据库运行的DDL脚本,而@JsonData定义了JSON格式的数据集。

DbUnitRule类的实际实现是:

package com.danhaywood.tdd.dbunit;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;import org.dbunit.IDatabaseTester;
import org.dbunit.JdbcDatabaseTester;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;import com.google.common.io.Resources;public class DbUnitRule implements MethodRule {@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public static @interface Ddl {String[] value();}@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD })public static @interface JsonData {String value();}private final Class<?> resourceBase;private IDatabaseTester databaseTester;private IDatabaseConnection dbUnitConnection;private Connection connection;private java.sql.Statement statement;public DbUnitRule(Class<?> resourceBase, Class<?> driver, String url, String user, String password) {this.resourceBase = resourceBase;try {databaseTester = new JdbcDatabaseTester(driver.getName(), url, user, password);dbUnitConnection = databaseTester.getConnection();connection = dbUnitConnection.getConnection();statement = connection.createStatement();} catch (Exception e) {throw new RuntimeException(e);}}@Overridepublic Statement apply(final Statement base, final FrameworkMethod method, final Object target) {return new Statement() {@Overridepublic void evaluate() throws Throwable {try {Ddl ddl = method.getAnnotation(Ddl.class);if (ddl != null) {String[] values = ddl.value();for (String value : values) {executeUpdate(Resources.toString(resourceBase.getResource(value), Charset.defaultCharset()));}}JsonData data = method.getAnnotation(JsonData.class);if (data != null) {IDataSet ds = new JSONDataSet(resourceBase.getResourceAsStream(data.value()));databaseTester.setDataSet(ds);}databaseTester.onSetup();base.evaluate();} finally {databaseTester.onTearDown();}}};}public java.sql.Connection getConnection() {return connection;}public void executeUpdate(String sql) throws SQLException {statement.executeUpdate(sql);}public ResultSet executeQuery(String sql) throws SQLException {return statement.executeQuery(sql);}public IDataSet jsonDataSet(String datasetResource) {return new JSONDataSet(resourceBase.getResourceAsStream(datasetResource));}public ITable createQueryTable(String string, String string2) throws DataSetException, SQLException {return dbUnitConnection.createQueryTable(string, string2);}
}

这使用了Lieven Doclo的JSONDataSet(为方便起见在此处复制):

import org.codehaus.jackson.map.ObjectMapper;import org.dbunit.dataset.*;
import org.dbunit.dataset.datatype.DataType;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;/*** DBUnit DataSet format for JSON based datasets. It is similar to the flat XML layout,* but has some improvements (columns are calculated by parsing the entire dataset, not just* the first row). It uses Jackson, a fast JSON processor.* <br/><br/>* The format looks like this:* <br/>* <pre>* {*    "&lt;table_name&gt;": [*        {*             "&lt;column&gt;":&lt;value&gt;,*             ...*        },*        ...*    ],*    ...* }* </pre>* <br/>* I.e.:* <br/>* <pre>* {*    "test_table": [*        {*             "id":1,*             "code":"JSON dataset",*        },*        {*             "id":2,*             "code":"Another row",*        }*    ],*    "another_table": [*        {*             "id":1,*             "description":"Foo",*        },*        {*             "id":2,*             "description":"Bar",*        }*    ],*    ...* }* </pre>** @author Lieven DOCLO*/
public class JSONDataSet extends AbstractDataSet {// The parser for the dataset JSON fileprivate JSONITableParser tableParser = new JSONITableParser();// The tables after parsingprivate List<ITable> tables;/*** Creates a JSON dataset based on a file* @param file A JSON dataset file*/public JSONDataSet(File file) {tables = tableParser.getTables(file);}/*** Creates a JSON dataset based on an inputstream* @param is An inputstream pointing to a JSON dataset*/public JSONDataSet(InputStream is) {tables = tableParser.getTables(is);}@Overrideprotected ITableIterator createIterator(boolean reverse) throws DataSetException {return new DefaultTableIterator(tables.toArray(new ITable[tables.size()]));}private class JSONITableParser {private ObjectMapper mapper = new ObjectMapper();/*** Parses a JSON dataset file and returns the list of DBUnit tables contained in* that file* @param jsonFile A JSON dataset file* @return A list of DBUnit tables*/public List<ITable> getTables(File jsonFile) {try {return getTables(new FileInputStream(jsonFile));} catch (IOException e) {throw new RuntimeException(e.getMessage(), e);}}/*** Parses a JSON dataset input stream and returns the list of DBUnit tables contained in* that input stream* @param jsonStream A JSON dataset input stream* @return A list of DBUnit tables*/@SuppressWarnings("unchecked")public List<ITable> getTables(InputStream jsonStream) {List<ITable> tables = new ArrayList<ITable>();try {// get the base object tree from the JSON streamMap<String, Object> dataset = mapper.readValue(jsonStream, Map.class);// iterate over the tables in the object treefor (Map.Entry<String, Object> entry : dataset.entrySet()) {// get the rows for the tableList<Map<String, Object>> rows = (List<Map<String, Object>>) entry.getValue();ITableMetaData meta = getMetaData(entry.getKey(), rows);// create a table based on the metadataDefaultTable table = new DefaultTable(meta);int rowIndex = 0;// iterate through the rows and fill the tablefor (Map<String, Object> row : rows) {fillRow(table, row, rowIndex++);}// add the table to the list of DBUnit tablestables.add(table);}} catch (IOException e) {throw new RuntimeException(e.getMessage(), e);}return tables;}/*** Gets the table meta data based on the rows for a table* @param tableName The name of the table* @param rows The rows of the table* @return The table metadata for the table*/private ITableMetaData getMetaData(String tableName, List<Map<String, Object>> rows) {Set<String> columns = new LinkedHashSet<String>();// iterate through the dataset and add the column names to a setfor (Map<String, Object> row : rows) {for (Map.Entry<String, Object> column : row.entrySet()) {columns.add(column.getKey());}}List<Column> list = new ArrayList<Column>(columns.size());// create a list of DBUnit columns based on the column name setfor (String s : columns) {list.add(new Column(s, DataType.UNKNOWN));}return new DefaultTableMetaData(tableName, list.toArray(new Column[list.size()]));}/*** Fill a table row* @param table The table to be filled* @param row A map containing the column values* @param rowIndex The index of the row to te filled*/private void fillRow(DefaultTable table, Map<String, Object> row, int rowIndex) {try {table.addRow();// set the column values for the current rowfor (Map.Entry<String, Object> column : row.entrySet()) {table.setValue(rowIndex, column.getKey(), column.getValue());}} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);}}}
}

我为此使用的库(即依赖项)为:

  • hsqldb 2.2.6
  • dbunit 2.4.8
  • 杰克逊1.9.3
  • slf4j-api-1.6.4,slf4j-nop-1.6.4
  • 谷歌番石榴10.0.1
  • junit 4.8

一如既往,欢迎发表评论。

参考:来自Dan Haywood博客的 JCG合作伙伴 Dan Haywood的dbUnit,JSON,HSQLDB和JUnit Rules的数据库单元测试 。

相关文章 :

  • JUnit 4.9(测试版3)中的规则
  • Spring 3使用JUnit 4进行测试– ContextConfiguration和AbstractTransactionalJUnit4SpringContextTests
  • Java RESTful API集成测试
  • 何时用集成测试替换单元测试
  • 我的测试和代码分析工具箱

翻译自: https://www.javacodegeeks.com/2012/01/db-unit-testing-with-dbunit-json-hsqldb.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374448.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round #321 (Div. 2) E. Kefa and Watch 线段树hash

E. Kefa and Watch Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/580/problem/EDescription One day Kefa the parrot was walking down the street as he was on the way home from the restaurant when he saw something glittering by…

python文字游戏源代码求年纪_Python实现猜年龄游戏代码实例

1. 在猜年龄的基础上编写登录、注册方法&#xff0c;并且把猜年龄游戏分函数处理&#xff0c;如 2. 登录函数 3. 注册函数 4. 猜年龄函数 5. 选择奖品函数 代码如下 import json real_age 18 prize_list [好迪洗发水, 绿箭侠, 小猪佩奇, 布娃娃, 再来一次!] import random us…

KVC 与 KVO

一、Key-Value Coding (KVC)键值编码 KVC&#xff0c;即是指 NSKeyValueCoding&#xff0c;一个非正式的 Protocol&#xff0c;提供一种机制来间接访问对象的属性。KVO 就是基于 KVC 实现的关键技术之一。 一个对象拥有某些属性。比如说&#xff0c;一个 Person 对象有一个 nam…

使用模拟的单元测试–测试技术5

我的最后一个博客是有关测试代码方法的一系列博客中的第四篇&#xff0c;演示了如何创建使用存根对象隔离测试对象的单元测试。 今天的博客探讨了有时被视为对立的技术&#xff1a;使用模拟对象进行单元测试。 同样&#xff0c;我使用了从数据库检索地址的简单方案&#xff1a;…

多线程中的volatile和伪共享

伪共享 false sharing&#xff0c;顾名思义&#xff0c;“伪共享”就是“其实不是共享”。那什么是“共享”&#xff1f;多CPU同时访问同一块内存区域就是“共享”&#xff0c;就会产生冲突&#xff0c;需要控制协议来协调访问。会引起“共享”的最小内存区域大小就是一个cache…

C语言代码规范(一)缩进与换行

一、缩进的空格数为4个。最好配置代码编辑器将TAB键设置为空格替换&#xff0c;避免出现另一个编辑器打开时格式变乱的情况。 例如Notepad设置 KEIL设置 二、“{” 和 “}”各自独占一行。 不规范例子&#xff1a; for(i 0; i < student_num; i) { if((score[i] > 0…

armv7 cortex a系列编程手册_AWTK能为现代GUI编程带来何种改变?

AWTK是一个伸缩性极强的嵌入式图形框架&#xff0c;它的诞生会给GUI编程研发工程师带来哪些改变&#xff1f;AWTK是一个伸缩性极强的嵌入式图形框架&#xff0c;可在Cortex-M3这样低端的单片机上运行&#xff0c;也可以在Cortex-A7/A8/A9等处理器&#xff0c;甚至DSP以及X86处理…

【转】各种概念POJO、JAVABEAN、DAO、DTO、PO、VO、BO、SSH、EJB

POJO&#xff08;pure old java object&#xff09; 是普通java类&#xff0c;有一些private的参数作为对象的属性&#xff0c;然后针对每一个参数定义get和set方法访问的接口。我看到这个定义&#xff0c;心里就有个疑问了&#xff0c;这个POJO跟JavaBean的定义怎么就这么像&a…

为什么要编写单元测试–测试技巧8

我对最近在“您应该测试什么”上的博客有很多反应&#xff0c;有些人出于各种原因同意我的想法&#xff0c;另一些人则认为建议某些类可能不需要单元测试是非常危险的。 已经处理了什么测试&#xff0c;今天的博客涉及为什么要编写单元测试&#xff0c;而今天的示例代码是基于一…

Git迁移 从SVN到Git

Migrating from SVN to Git 首先我们需要在Stach或者GitHub上新建一个Repository, 拿到它的URL。 接下来参照如下步骤 : At first we should create a new git repository at Stash and get the repository URL, and then follow below steps: 1. 切换到本地git工作目录 chang…

C语言代码规范(二)空格

一、逗号, 之后加空格 printf("error! score[%d] %d\n", i, score[i]); 二、分号; 之后加空格 for(i 0; i < student_num; i) 三、关系运算符<、<、>、>、、! 前后加空格 if( (score[i] > 0) && (score[i] < 100) ) 四、赋值运算符…

c++ 多重背包状态转移方程_动态规划入门——详解经典问题零一背包

本文始发于个人公众号&#xff1a;TechFlow&#xff0c;原创不易&#xff0c;求个关注今天是周三算法与数据结构专题的第12篇文章&#xff0c;动态规划之零一背包问题。在之前的文章当中&#xff0c;我们一起探讨了二分、贪心、排序和搜索算法&#xff0c;今天我们来看另一个非…

Discuz! 的编码规范

前言 本规范由编程原则组成&#xff0c;融合并提炼了开发人员长时间积累下来的成熟经验&#xff0c;意在帮助形成良好一致的编程风格。适用范围 如无特殊说明&#xff0c;以下规则要求完全适用于Discuz!项目&#xff0c;同时也可大部分适用于COMSENZ旗下其他PHP项目。标准化的重…

C语言代码规范(三)if语句

一、整型变量与0比较 许多人为了一时之便&#xff0c;模仿布尔变量风格写为如下代码 if(value) {... }if(!value) {... } 应当用 或 ! 来与0比较 if(0 value) {... }if(0 ! value) {... } 二、当if内的语句是与常量进行比较时&#xff0c;常量为左值&#xff0c;变量为右…

6月24 面向对象的设计原则-----工厂模式和单列模式

工厂模式&#xff1a; 工厂模式就是专门负责将大量有共同接口的类实例化&#xff0c;而且不必事先知道每次是要实例化哪一个类的模式。它定义一个用于创建对象的接口&#xff0c;由子类决定实例化哪一个类。 工厂模式相当于创建实例对象的new&#xff0c;经常要根据类Class生成…

LeetCode Subsets

原题链接在这里&#xff1a;https://leetcode.com/problems/subsets/ 题目&#xff1a; Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subset must be in non-descending order.The solution set must not contain duplicate su…

使用ThreadPoolExecutor并行化独立的单线程任务

Java SE 5.0中引入的任务执行框架是简化多线程应用程序的设计和开发的巨大飞跃。 该框架提供了用于管理任务概念&#xff0c;管理线程生命周期及其执行策略的工具。 在此博客文章中&#xff0c;我们将描述该框架的功能&#xff0c;灵活性和简单性&#xff0c;以展示一个简单的用…

python定义一个圆_Python-矩形和圆形

原博文 2019-11-11 12:34 − Exercise 15.1. 定义一个叫做Circle 类&#xff0c;类的属性是圆心 (center) 和半径 (radius) , 其中&#xff0c;圆心 (center) 是一个 Point 类&#xff0c;而半径 (radius) 是一个数字。 实例化一个圆心 (center) 为 (150, 100) &#xff0c;半…

C语言代码规范(四)命名规则

一、宏定义全部字母大写&#xff0c;单词间下划线间隔 #define FLASH_PAGE_SIZE 256 #define FLASH_SECTOR_SIZE (4 * 1024) #define FLASH_BLOCK_SIZE (64 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) 二、const修饰的常量全部字母大写&#xff0c;单词间…

Forbidden You don't have permission to access / on this server PHP

Forbidden You dont have permission to access / on this server PHP 在新安装的谷歌游览器里&#xff0c;打不了PHP网站了&#xff0c;错误显示&#xff1a; Forbidden You dont have permission to access / on this server. 原因还是配置权限问题 解决办法&#xff1a; wa…