assertJ-db 科普

2024-06-19 14:28
文章标签 db 科普 database assertj

本文主要是介绍assertJ-db 科普,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

今日我们看看 java 大名鼎鼎的 assertj 是怎么做断言的

数据库断言

在实际的测试中我们总是跟业务打交道的。跟业务打交道一般很难避免验证数据库中的东西。尤其在接口测试中,一个常见的例子是你测试一个下单的接口。 接口返回可能就是成功过或者失败。你无法从返回值中判断订单的细节是否创建成功。这时候一般要查询数据库做断言。

demo

来看一下例子。

Request sql = new Request(dataSource,"select * from data where name = '"+data.getName()+"'");
assertThat(sql).row().column("name").value().isEqualTo(data.getName()).column("meta").value().isEqualTo(data.getMeta()).column("line_number").value().isEqualTo(data.getLineNumber()).column("size").value().isEqualTo(data.getSize()).column("type_id").value().isGreaterThan(data.getTypeId()).column("src_type_id").value().isEqualTo(data.getSrcTypeId());Request resourceSql = new Request(dataSource,"select * from resource where id = "+createdData.getResourceId()+"");
assertThat(resourceSql).row()
.column("uri").value().isEqualTo(data.getUri())
.column("protocol").value().isEqualTo(data.getProtocol().getValue());Request sql = new Request(dataSource,"select * from plan where name='"+copyPlanName+"'");
assertThat(sql).as("数据库中没有被保存的plan").hasNumberOfRows(1);

OK,assertJ-db 有一个对象叫 Request,它是对数据库请求的封装。创建的时候你需要传递一个 sql 和 dataSource(我使用的是 mybatis 的 dataSource)。接着就可以使用 assertJ 丰富的 data flow 式 API 进行断言了。 篇幅有限,介绍几个常用的使用方法。

元素

assertJ-db 有 4 个维度的元素用来断言。分别是:

  1. Table:表
  2. column:列
  3. row:行
  4. value:具体的一个字段的值

看我们第一个例子的代码中有如下一段:

Request sql = new Request(dataSource,"select * from data where name = '"+data.getName()+"'");assertThat(sql).row().column("name").value().isEqualTo(data.getName())

这里面我们分别取到了第一行 (row() 默认取第一行,也可以指定第几行) 中的名字叫 name 的列 (column 接受 string 类型的列名参数) 的值 (value)。因为我们是以 Request 的方式进行断言,所以就没有 table 什么事了. 我只举个简单的例子,这 4 种元素互相可以有各种组合的断言大家可以慢慢的探索。我只列出我个人最长用的使用方式。

compare api

assertJ-db 提供丰富的 compare api 来定制我们的断言。 我们看如下的代码:

assertThat(request).row(1).value().isEqualTo(2).value().isEqualTo(DateValue.of(1981, 10, 12)).hasValues("2", "1981-10-12", "October", "11", "00:41:08", null).value().isEqualTo("1.77").isNotEqualTo("1.78").value("size").isNotZero().isGreaterThan(1.5).isGreaterThanOrEqualTo(1.77).isLessThan(2).isLessThanOrEqualTo(1.77).value().isNull()Request sql = new Request(dataSource,"select * from plan where name='"+copyPlanName+"'");
assertThat(sql).hasNumberOfRows(1);

好有很多的 compare api 来为我们的 4 个元素进行验证。我不一一列举了。 它可以给我们各种维度的断言方式。

Request

在之前的介绍中我们总能看到 Request 的身影,这是我们最常用的断言模式。直接以一个 sql 筛选出要断言的数据。如下:

Request sql = new Request(dataSource,"select * from plan where name='"+copyPlanName+"'");
assertThat(sql).hasNumberOfRows(1).row().column("name").value().isEqualTo(copyPlanName)

Request 的目的就是根据 sql 筛选出我们要断言的数据,然后使用我们上面说的元素和 compare api 进行断言的工作。

Changes

之前我写监控式数据管理策略的时候写过 Changes 的使用。现在我再说一下吧。按惯例先看例子

public static Changes initChanges() {Changes change = null;if(changes != null) {change = changes;} else if(tables != null) {change = new Changes(tables);} else if(dataSource != null) {change = new Changes(dataSource);} else {if(source == null) {throw new ChangesException("you have not regiter any source to create changes. you should register changes, datasource,source or tables so that you can init changes to handle test datas");}change = new Changes(source);}return change;}

上面是我的数据管理部分初始化 changes 的代码。可以看到我们可以把 dataSource 传进去用来 diff 整个数据库。 也可以把 4 重元素的 table 传进去用来 diff 某一个或某一些表。changes 有两个方法,一个叫 start point,用来记录初始数据的状态,另一个叫 end point。用来记录结束的数据的状态。 changes 就是去 diff 这两个时间点的数据变化。不多说直接上一个例子:

Changes changes = new Changes(dataSource);changes.setStartPointNow();                // Start point (the moment when the changes start to be taken into account)makeChangesInTheData();changes.setEndPointNow();                  // End point (the moment when the changes stop to be taken into account)assertThat(changes).change()                               // First change.isCreation()                       // Assertion on the first change.rowAtStartPoint()                  // Row at the start point.doesNotExist()                 // Assertion on the row at start point of the first change.returnToChange()                   // Go back to the change.rowAtEndPoint()                    // Row at the end point.hasNumberOfColumns(6)          // Assertion on the row at end point of the first change.exists()                       // Another assertion on the same row.value(1)                       // Value at index 1 of the row at end point of the first change.isEqualTo("McGuiness")     // Assertion on the value.returnToRow()                  // Go back to the row.returnToChange()                   // Go back to the change.change()                               // Next change.rowAtEndPoint()                    // Row at end point of this change.hasValues(1, "Hewson", "Paul David", "Bono Vox", "1960-05-10", 1.75).column("surname")                  // Column with name is "surname" of the second change (note that returnToChange() is not mandatory).isModified()                   // Assertion on column.hasValues("Bono","Bono Vox").column()                           // Next column.isNotModified()                // Assertion on the column.valueAtEndPoint()              // Value at end point in the column after "surname" ("birth") of the second change.isEqualTo(DateValue.of(1960, 5, 10)).ofDeletion()                           // All the changes of deletion (note that the returnToXxxx() methods are not mandatory).change()                           // First change of these changes of deletion.isOnTable("albums").hasPksValues(15).changeOnTableWithPks("members", 5)     // Change with primary key 5 on "members" table.isCreation().rowAtEndPoint()                // Row at end point of change with primary key 5 on "members" table .hasValues(5, "McGuiness", "Paul", null,"1951-06-17", null);

这是 github 上官方的例子。如果你可以完全掌控你们的数据库,使用 changes 做断言不失为一个选择。下面再看一个例子

for(Change change:changeList){ChangeType type = change.getChangeType();String tableName = change.getDataName();if("CREATION".equals(type.name())){String id = change.getRowAtEndPoint().getValuesList().get(0).getColumnName();Object value = change.getRowAtEndPoint().getValuesList().get(0).getValue();String sql = "delete from "+tableName+" where "+id+" = "+value+"";deletionSQLs.add(sql);}else if("DELETION".equals(type.name())){String sql = "insert into "+tableName+" values(";List<Value> valuesList = change.getRowAtStartPoint().getValuesList();for(Value value : valuesList){Object columenValue = value.getValue();sql = sql + "'" +columenValue+"',";}sql = sql.substring(0, sql.length()-1);sql = sql +")";insertSQLs.add(sql);}else if("MODIFICATION".equals(type.name())){String sql = "update "+tableName+" SET ";List<Value> valuesList = change.getRowAtStartPoint().getValuesList();for(Value value : valuesList){Object columenValue = value.getValue();String columnName = value.getColumnName();sql = sql + columnName +"='"+columenValue+"' ,";}sql = sql.substring(0, sql.length()-1);sql = sql + " where "+valuesList.get(0).getColumnName()+" = "+valuesList.get(0).getValue();updateSQLs.add(sql);}}

除了断言还可以像上面一样根据 changes 取出所有变化的数据信息并拼成 sql。大家可以根据自己的需要取得这些信息。例如在自动化中 hack 一些信息。假如像我一样,拼出所有变化数据的 sql。这样在某些特定的 case,例如会影响其他 case 运行的一些场景。在 case 运行后自动拼出 sql 把数据恢复回去。 或者在有些自动化中你是获取不到一些信息的情况,例如后台发生了一些操作,而你并不知道或者不容易获取这些操作信息。就可以使用 changes 把这些信息都取出来,甚至根据需要 hack 一些东西。有兴趣的同学可以翻我之前写的监控式数据管理的帖子,看看我是怎么用 changes 做数据恢复的

Table

一直没说 table 的事。给个例子把,我几乎不怎么用。

Source source = new Source("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "user", "password");Table table = new Table(source, "members");assertThat(table).column("name").hasValues("Hewson", "Evans", "Clayton", "Mullen");

Source

在 assertJ-db 中,无论是 changes,request 还是 table 等等。初始化的时候都是要传递一个 Source 作为参数的。其实就是你要传递一个数据的 datasouce 给 assertJ-db。 其实在 assertJ-db 中内置了一个 source,你可以看到如下的调用

Source source = new Source("jdbc:mysql://172.27.1.219:3306/dango?useUnicode=yes&amp;characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull", "write", "!@#$1234%^&*5678ABCDabcd");
Changes changes = new Changes(source);

但是~注意了,但是 assertJ-db 内置的这种方式慢的要死。强烈不建议使用。 我是使用的 mybatis 的 datasource 搞的,因为我有些数据库操作是用 mybatis 搞的。

总结

好了,assertJ-db 科普帖到此结束。

 更多内容欢迎来到我的知识星球:
 

这篇关于assertJ-db 科普的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1075284

相关文章

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

解密SQL查询语句执行的过程

《解密SQL查询语句执行的过程》文章讲解了SQL语句的执行流程,涵盖解析、优化、执行三个核心阶段,并介绍执行计划查看方法EXPLAIN,同时提出性能优化技巧如合理使用索引、避免SELECT*、JOIN... 目录1. SQL语句的基本结构2. SQL语句的执行过程3. SQL语句的执行计划4. 常见的性能优

SQL Server 中的 WITH (NOLOCK) 示例详解

《SQLServer中的WITH(NOLOCK)示例详解》SQLServer中的WITH(NOLOCK)是一种表提示,等同于READUNCOMMITTED隔离级别,允许查询在不获取共享锁的情... 目录SQL Server 中的 WITH (NOLOCK) 详解一、WITH (NOLOCK) 的本质二、工作

MySQL 强制使用特定索引的操作

《MySQL强制使用特定索引的操作》MySQL可通过FORCEINDEX、USEINDEX等语法强制查询使用特定索引,但优化器可能不采纳,需结合EXPLAIN分析执行计划,避免性能下降,注意版本差异... 目录1. 使用FORCE INDEX语法2. 使用USE INDEX语法3. 使用IGNORE IND

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

2025版mysql8.0.41 winx64 手动安装详细教程

《2025版mysql8.0.41winx64手动安装详细教程》本文指导Windows系统下MySQL安装配置,包含解压、设置环境变量、my.ini配置、初始化密码获取、服务安装与手动启动等步骤,... 目录一、下载安装包二、配置环境变量三、安装配置四、启动 mysql 服务,修改密码一、下载安装包安装地

MySQL CTE (Common Table Expressions)示例全解析

《MySQLCTE(CommonTableExpressions)示例全解析》MySQL8.0引入CTE,支持递归查询,可创建临时命名结果集,提升复杂查询的可读性与维护性,适用于层次结构数据处... 目录基本语法CTE 主要特点非递归 CTE简单 CTE 示例多 CTE 示例递归 CTE基本递归 CTE 结

MySQL多实例管理如何在一台主机上运行多个mysql

《MySQL多实例管理如何在一台主机上运行多个mysql》文章详解了在Linux主机上通过二进制方式安装MySQL多实例的步骤,涵盖端口配置、数据目录准备、初始化与启动流程,以及排错方法,适用于构建读... 目录一、什么是mysql多实例二、二进制方式安装MySQL1.获取二进制代码包2.安装基础依赖3.清

详解MySQL中JSON数据类型用法及与传统JSON字符串对比

《详解MySQL中JSON数据类型用法及与传统JSON字符串对比》MySQL从5.7版本开始引入了JSON数据类型,专门用于存储JSON格式的数据,本文将为大家简单介绍一下MySQL中JSON数据类型... 目录前言基本用法jsON数据类型 vs 传统JSON字符串1. 存储方式2. 查询方式对比3. 索引