使用自定义断言丰富测试代码

受GeeCON会议期间@tkaczanowski演讲的启发,我决定仔细研究AssertJ库的自定义断言。

在我的“骰子”游戏中,我创建了一个“机会”,它是骰子的任意组合,其分数是所有骰子的总和。 这是相对简单的对象:

class Chance implements Scorable {@Overridepublic Score getScore(Collection<Dice> dice) {int sum = dice.stream().mapToInt(die -> die.getValue()).sum();return scoreBuilder(this).withValue(sum).withCombination(dice).build();}
}public interface Scorable {Score getScore(Collection<Dice> dice);
}

在我的测试中,我想看看如何计算不同骰子组合的分数。 我从简单开始(实际上只有一个):

public class ChanceTest {private Chance chance = new Chance();@Test@Parameterspublic void chance(Collection<Dice> rolled, int scoreValue) {// arrangeCollection<Dice> rolled = dice(1, 1, 3, 3, 3);// actScore score = chance.getScore(rolled);// assertassertThat(actualScore.getScorable()).isNotNull();assertThat(actualScore.getValue()).isEqualTo(expectedScoreValue);assertThat(actualScore.getReminder()).isEmpty();assertThat(actualScore.getCombination()).isEqualTo(rolled);}}

测试中验证了单个概念(得分对象)。 为了提高分数验证的可读性和可重用性,我将创建一个自定义断言。 我希望我的断言像其他任何AssertJ断言一样被使用,如下所示:

public class ChanceTest {private Chance chance = new Chance();@Testpublic void scoreIsSumOfAllDice() {Collection<Dice> rolled = dice(1, 1, 3, 3, 3);Score score = chance.getScore(rolled);ScoreAssertion.assertThat(score).hasValue(11).hasNoReminder().hasCombination(rolled);}
}

为了实现这一点,我需要创建一个从org.assertj.core.api.AbstractAssert扩展的ScoreAssertion类。 该类应具有公共的静态工厂方法和所有必需的验证方法。 最后,实现可能如下图所示。

class ScoreAssertion extends AbstractAssert<ScoreAssertion, Score> {protected ScoreAssertion(Score actual) {super(actual, ScoreAssertion.class);}public static ScoreAssertion assertThat(Score actual) {return new ScoreAssertion(actual);}public ScoreAssertion hasEmptyReminder() {isNotNull();if (!actual.getReminder().isEmpty()) {failWithMessage("Reminder is not empty");}return this;}public ScoreAssertion hasValue(int scoreValue) {isNotNull();if (actual.getValue() != scoreValue) {failWithMessage("Expected score to be <%s>, but was <%s>", scoreValue, actual.getValue());}return this;}public ScoreAssertion hasCombination(Collection<Dice> expected) {Assertions.assertThat(actual.getCombination()).containsExactly(expected.toArray(new Dice[0]));return this;}
}

创建这样的断言的动机是拥有更多可读性和可重用性的代码。 但是它要付出一些代价–需要创建更多代码。 在我的示例中,我知道我很快就会创建更多的Scorables并且需要验证它们的评分算法,因此创建额外的代码是合理的。 增益将可见。 例如,我创建了一个NumberInARow类,该类计算给定骰子组合中所有连续数字的分数。 分数是具有给定值的所有骰子的总和:

class NumberInARow implements Scorable {private final int number;public NumberInARow(int number) {this.number = number;}@Overridepublic Score getScore(Collection<Dice> dice) {Collection<Dice> combination = dice.stream().filter(value -> value.getValue() == number).collect(Collectors.toList());int scoreValue = combination.stream().mapToInt(value -> value.getValue()).sum();Collection<Dice> reminder = dice.stream().filter(value -> value.getValue() != number).collect(Collectors.toList());return Score.scoreBuilder(this).withValue(scoreValue).withReminder(reminder).withCombination(combination).build();}
}

我从连续检查两个5的测试开始,但是我已经错过了断言( hasReminder ,因此改进了ScoreAssertion 。 我继续通过其他测试更改断言,直到获得可以在测试中使用的非常完善的DSL:

public class NumberInARowTest {@Testpublic void twoFivesInARow() {NumberInARow numberInARow = new NumberInARow(5);Collection<Dice> dice = dice(1, 2, 3, 4, 5, 5);Score score = numberInARow.getScore(dice);// static import ScoreAssertionassertThat(score).hasValue(10).hasCombination(dice(5, 5)).hasReminder(dice(1, 2, 3, 4));}@Testpublic void noNumbersInARow() {NumberInARow numberInARow = new NumberInARow(5);Collection<Dice> dice = dice(1, 2, 3);Score score = numberInARow.getScore(dice);assertThat(score).isZero().hasReminder(dice(1, 2, 3));}
}public class TwoPairsTest {@Testpublic void twoDistinctPairs() {TwoPairs twoPairs = new TwoPairs();Collection<Dice> dice = dice(2, 2, 3, 3, 1, 4);Score score = twoPairs.getScore(dice);assertThat(score).hasValue(10).hasCombination(dice(2, 2, 3, 3)).hasReminder(dice(1, 4));}
}

更改后的断言如下所示:

class ScoreAssertion extends AbstractAssert<ScoreAssertion, Score> {protected ScoreAssertion(Score actual) {super(actual, ScoreAssertion.class);}public static ScoreAssertion assertThat(Score actual) {return new ScoreAssertion(actual);}public ScoreAssertion isZero() {hasValue(Score.ZERO);hasNoCombination();return this;}public ScoreAssertion hasValue(int scoreValue) {isNotNull();if (actual.getValue() != scoreValue) {failWithMessage("Expected score to be <%s>, but was <%s>",scoreValue, actual.getValue());}return this;}public ScoreAssertion hasNoReminder() {isNotNull();if (!actual.getReminder().isEmpty()) {failWithMessage("Reminder is not empty");}return this;}public ScoreAssertion hasReminder(Collection<Dice> expected) {isNotNull();Assertions.assertThat(actual.getReminder()).containsExactly(expected.toArray(new Dice[0]));return this;}private ScoreAssertion hasNoCombination() {isNotNull();if (!actual.getCombination().isEmpty()) {failWithMessage("Combination is not empty");}return this;}public ScoreAssertion hasCombination(Collection<Dice> expected) {isNotNull();Assertions.assertThat(actual.getCombination()).containsExactly(expected.toArray(new Dice[0]));return this;}
}

我真的很喜欢自定义AssertJ断言的想法。 在某些情况下,它们将提高我的代码的可读性。 另一方面,我很确定不能在所有情况下使用它们。 特别是在那些可重用机会很小的地方。 在这种情况下,可以使用带有分组断言的私有方法。

你有什么意见?

资源资源

  • https://github.com/joel-costigliola/assertj-core/wiki/Creating-specific-assertions
  • @tkaczanowski的断言演变

翻译自: https://www.javacodegeeks.com/2014/05/spice-up-your-test-code-with-custom-assertions.html

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

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

相关文章

js判断数据类型

在js中如果想判断某种数据的数据类型&#xff0c;一般使用typeof方法 var str "string";var num 1;var bool true;var u;var n null;var nan NaN;var obj new Object();var fn function () {};console.log(typeof str); //stringconsole.log(typeof num); //n…

Elasticsearch 索引别名的使用

几种常用方式&#xff1a; 1.定时更新别名指向&#xff0c;更新后原索引可删除或保留 POST /_aliases {"actions": [{"add": {"alias": "logs_current","index": "logs_2018-10"}},{"remove": {"a…

Python的win32serviceutil之疑似BUG

郑昀玩聚SR 20090515 1、现象&#xff1a; 用Python的win32serviceutil控制Windows Service启动、停止、重启时&#xff0c;如下代码一般是没问题的。 import win32serviceutil def service_manager(action, machine, service): if action stop: win32serviceutil…

JavaFX 8u20天的未来过去(始终在最前面)

自从我发布有关JavaFX的主题以来已经有很长时间了。 因此&#xff0c;如果您仍在追随&#xff0c;那就太棒了&#xff01; 介绍 在这篇博客文章中&#xff0c;我想写一篇关于从JavaFX 8 update 20开始的非常酷的功能的博客&#xff0c;该功能使您的应用程序始终位于其他应用程…

cocos creator实战-(三)简单例子摇杆控制角色移动

&#xff08;待完善&#xff0c;给玩家加上摄像机跟随效果&#xff09; 1、stick监听cc.Node.EventType.TOUCH_MOVE事件&#xff0c;获取tick移动的坐标和朝向&#xff0c;限制移动的范围 2、根据stick的朝向&#xff0c;每帧更新player的位置和方向 // 摇杆代码 joy_stick.jsc…

统计数组中重复元素个数

/*** 循环统计数组或集合中的重复元素个数* param args*/public static void main(String[] args) {Map<String, Integer> map new HashMap<String, Integer>();String[] ss {"白","黑","绿","白"};for (int i 0; i &l…

火狐 和 IE 透明度的设置。

javascript 中设置如下。 Current 是一个div层。 if(Current.filters undefined) // fire fox { Current.style.MozOpacity 0.75; } else { // ie Current.filters.alpha.opacity…

使用Java 8.0进行类型安全的依赖项注入

所以我有时真的很想念旧学校的依赖注入。 当Spring仍然“轻量级”时&#xff0c;我们很高兴地使用“ 一天学习 ” Spring bean xml配置在application.xml文件中配置了所有bean。 缺点当然是类型安全性的损失。 我可以想到很多测试用例&#xff0c;这些用例的唯一目的是引导Spri…

a标签点击跳转失效--IE6、7的奇葩bug

一般运用a标签包含img去实现点击图片跳转的功能&#xff0c;这是前端经常要用到的东西。 今天遇到个神奇的bug&#xff1a;如果在img上再包裹一层div&#xff0c;而且div设置了width和height&#xff0c;则图片区域点击时&#xff0c;无任何响应。 出现这个bug的条件是&#…

python 数据类型之间的转换

Number数据类型转换 # ###强制类型换 Number (int float bool complex) var1 68 var2 6.89 var3 False var4 3-4j var5 "12345678" var6 "qwe123"#(1) int 强制转换成整型 res int(var2) res int(var3) #res int(var4) #TypeError: cant convert …

php构造数组,并把多数组插入php文件

晚上做的一点东西&#xff0c;发出来大家共享下&#xff01; Code<?php //php 链接数据库mysql_connect("localhost", "root", "hicc") or die("Could not connect: " . mysql_error());mysql_select_db("babyker");$re…

针对新手的Java EE7和Maven项目–第6部分

从前面的部分恢复 第1 部分 &#xff0c; 第2 部分 &#xff0c; 第3 部分 &#xff0c; 第4 部分 &#xff0c; 第5部分 在上一篇文章&#xff08;第5部分&#xff09;中&#xff0c;我们发现了如何使用Arquillian&#xff08;我们的EJB服务&#xff09;进行单元测试&#xf…

1. 整除及其性质

整除的定义&#xff1a; 若整数 a 除以非零整数 b &#xff0c;商为整数且余数为零&#xff0c;即 a 能被 b 整除&#xff0c;记做 b | a&#xff0c;读作&#xff1a;b 整除 a 或 a 能被 b 整除。a 叫做 b 的倍数&#xff0c; b 叫做 a 的因数。 整除基本性质&#xff1a; 1. …

echarts 地图 免费离线js,json包分享

最近&#xff0c;项目中需要用到地图&#xff0c;由于项目的特殊性&#xff0c;只能使用内网获取数据。 然而&#xff0c;echarts官网上的离线地图包&#xff08;http://echarts.baidu.com/download-map.html&#xff09;早在一年前就不支持下载了&#xff0c;推荐使用地图API…

WPF 中的设备无关单位

WPF窗体以及内部的所有元素都是采用设备无关的单位来衡量的。一个设备无关单位定义为1英寸的96分之一&#xff0c;即1/96 inch。 假定我们创建了一个WPF按钮&#xff0c;其大小为96x96个单位&#xff0c;如果使用标准Windows的DPI设置&#xff08;96dpi&#xff09;&#xff0…

收藏网站制作常用经典css.div.布局.设计实例打包下载(下方有其他链接)

http://www.aa25.cn/234.shtml 转载于:https://www.cnblogs.com/asia/archive/2009/05/20/1467772.html

Java 8 Friday:大多数内部DSL已过时

在Data Geekery &#xff0c;我们喜欢Java。 而且&#xff0c;由于我们真的很喜欢jOOQ的流畅的API和查询DSL &#xff0c;我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 Java 8星期五 每个星期五&#xff0c;我们都会向您展示一些不错的教程风格的Java 8新功能&#…

Starter pom

以下图片是引用书籍内容&#xff1a; 比如你在用boot写一个web项目&#xff0c;在maven中你会导入&#xff1a; <!-- 导入spring boot的web支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-we…

上传图片截图预览控件不显示cropper.js 跨域问题

上传图片到图片服务器&#xff0c;因为域名不同&#xff0c;多以会有跨域问题。 No Access-Control-Allow-Origin header is present on the requested resource. Origin http://img.xxx.com is therefore not allowed access. 照看代码发现&#xff0c;cropper.js里面对图片的…

在Spring JDBC中添加C3PO连接池

连接池是一种操作&#xff0c;其中系统会预先初始化将来要使用的连接。 这样做是因为在使用时创建连接是一项昂贵的操作。 在本文中&#xff0c;我们将学习如何在Spring JDBC中创建C3P0连接池&#xff08;某人未使用休眠&#xff09;。 Pom.xml <dependency><groupI…