软件测试是执行的软件以验证代码状态(state testing)或事件序列(behavior testing)符合预期。html
软件单元测试帮助开发人员验证程序的部分逻辑是正确的。java
单元测试在计算机编程中单元测试(Unit Testing)又称为模块测试,是针对代码模块(软件设计的最小单位)来进行正确性检验的测试工做。在过程化编程多是整个模块、但更可能是函数或过程等;对于面向对象编程一般是整个接口,好比类,也多是方法。使用代码来测试代码,关注状态和行为测试。单元测试所测试的百分比一般被称为测试覆盖率。。python
单元测试基于测试用例进行组织。理想的状况下,测试用例互相独立,为此使用桩、mock、fake、服务虚拟化和测试框架等,一般由开发或者白盒测试人员书写。对于数据库和缓存等操做,传统建议用mock的方式进行单元测试。可是依赖较多的状况下mock比较耗时耗力,为此尽可能采用本地构建实际数据、缓存等中间件的方式,尽可能少使用mock,对于网络依赖,则尽可能采用服务虚拟化。android
测试代码与实际代码分开。
git
单元测试主要针对各类输入组合进行测试,经常使用的方法有等价类划分和边界值等。同时特别关注控制流和数据流,经过关注if、for、switch、while等分支条件,尽可能提升覆盖率。另外单元测试要格外关注异常处理逻辑。github
单元测试重视覆盖率,可是不为了追求覆盖率而不顾实际状况。一般通常的公司在覆盖率50%左右能达到比较好的性价比。追求90%以上的覆盖率一般比较有难度,即使是google之类的领导性的公司,代码覆盖率也只达到了80%多。web
单元测试能提早发现一些集成、系统测试阶段的问题,可是不能替代后续测试。数据库
单元测试在代码高内聚松耦合及公共库提取完整等状况比较容易进行测试。编程
单元测试一般与小提交、每次提交触发测试等持续集成配合使用。flask
单元测试自动生成用例是较前沿的技术,相关商业工具备:AgitarOne、Jtest 、SilverMark Test Mentor
开源工具备:CodePlex AnalytiX、EvoSuite、Randoop、Palus、Daikon, Eclat, Javalanche, Java PathFinder, JCrasher, jWalk, Korat, RecGen和ReCrash等。固然有很多公司直接用python控制各类语言生成单元测试用例。
单元调用系统公共方法到产生结果之间的部分。结果能够是:返回值(非void)、系统的状态或行为改变、不受控制的第三方系统调用。单元测试的范围能够小到一个方法,大到多个类。测试单元不是越小越好,把工做单元缩到最小,最后会不得不伪造一堆东西,这些东西并非使用公共API的真实最终结果,而是生成结果过程当中的一些中间状态。近年来,单元测试有测试功能、性能、安全等趋向,好比安卓中就有本地单元测试和基于仿真的单元测试。
参考资料:https://en.wikipedia.org/wiki/Unit_testing
单元测试的组织:AAA(arrange, act, and assert) 可能还有After。
哪些项目最适合单元测试?
* 平台类库性质的项目
* 规模大的,须要长时间维护的
* 正确性要求极高的项目
哪些代码须要测试?
* UI层
* UI Business
* 业务层
* 数据层
通常只须要测公开方法
单元测试的前提
* 程序层次结构清楚
* 层间解耦
* 外部依赖小
最最糟糕的被测方法
* 业务代码混在UI层方法中
* 数据库操做混在业务代码中
* 外部依赖不少
UI层的测试
* 不须要具体的业务实现
* 能够看到在不一样业务下的UI表现
* 关键字: 静态Mock
业务层的测试
* 不须要具体的数据层
* 不须要UI层来验证
* 关键字: 动态Mock
业务层的测试,为了去除依赖,mock是必须的。
但在使用每个Mock前,请想一想,是否能够经过重构,摆脱这个依赖。
先定好接口,而后对于具体实现,就可使用TDD
自动化
关键字:人工干预(好比须要网络链接、数据库等),经过Mock,去除干预。
关键字:DailyBuild 。
好的测试:可重复、独立的
开源的以xunit为主。商业工具备Typemock Isolator.NET/Isolator++, TBrun, JustMock, Parasoft Development Testing (Jtest, Parasoft C/C++test, dotTEST), Testwell CTA++ and VectorCAST/C++等。
Python: unittest、pytest(推荐)、nose
Java: Junit(推荐)、TestNG、Spock。能够理解Junit是TestNG的子集,可是Junit使用更普遍。Java的主流测试框架是JUnit和TestNG。前者用户最多,对mock框架兼容好。后者可读性更好。
不写单元测试的借口:
* 编写单元测试太花时间了
* 运行测试的时间太长了
* 测试代码并非个人工做
* 代码太复杂了,依赖太多,不能作单元测试
* 没有单元测试,这些问题照样能发现。
1, 尽早发现bug,下降bug修复成本。在比较理想的状况下,单元测试能发现占比40%左右的BUG。
2, 验证功能符合预期
3, 文档化
4, 确认代码修改没有影响其余功能。
5, 理解当前系统行为
6, 验证第三方代码。
7, 简化集成(自底向上)
8,TDD
9,加强自信,减小没必要需要的时间浪费。
10,及早发现架构等设计方面的问题。
单元测试常见的问题:
•测试毫无心义
•测试不稳定
•测试须要很长的时间来执行
•测试没有充分覆盖的代码
•测试耦合太紧,修改麻烦
•测试准备工做复杂。
• [F]ast
测试执行控制在3分钟之内。尽可能减小依赖。
• [I]solated
• [R]epeatable
好比当前时间。必要时可使用Sandbox。
• [S]elf-validating
print、System.out.println(),测试框架能够减小代码、减小没必要要的输出。要能自验证、自安排、自动化。一般与buildbot、jenkins、TeamCity之类的集成系统,有提交的时候触发构建与测试,甚至持续交付。
固然还有简单Simple的意思。能简单地书写用例、定位问题。
• [T]imely
及时更新测试代码,免得后期对代码生疏了更耗时。必要时有评审也自动系统监控。后期补单元测试的价值这没有前期大。
Right-BICEP
Right 结果正确
B 边界条件
I 反向关系
C 经过其余方法交叉检查结果
E 错误条件
P 性能
边界值:
•虚假或不一致的输入值,如文件名
“* W:!点¯x\&GI / W $→> $ G / H#@ WQ。
•格式错误,如电子邮件地址缺乏顶级域名( fred@foobar. )
•计算中可能致使数值溢出。
•空或没有值,如0,0.0,"”或null。
•值远远超过合理的指望,好比年龄180岁。
•列表中有不应有的重复名单。
•对有序列表进行排序。
•乱序
涉及Conformance、Ordering、Range、Reference、Existence、Cardinality、Time(相对,好比、绝对时间、并发)。
好比索引:
•起始和结束索引具备相同的值
•起始值比结束值大
•指数为负
•结束值超过容许
•不能匹配具体项。
引用:
•跨范围引用了什么
•外部依赖
•是否须要对象在某一状态
•必须存在的任何其余条件
反向关系
好比用加法验证加法,用除法验证乘法。数据库的插入与查询、加密与解密等。百分比的总数必须等于1。
错误条件:
内存满、磁盘满、网线断、邮件进入黑名单、程序崩溃、时间错误、系统负载高、色彩位数低、分辨率低等。
性能:单元测试中计时。JUnitPerf JMeter等。
以上部分主要来源junit8,须要有空时再次阅读。
简单的getter和setter之类代码一般不须要测试。JVM相关内容一般能够认为是可靠的。对已有代码的测试,一般从出错最多的地方开始。
要把测试当作设计工具,不只仅是质量保证工具。
测试驱动开发概念参考:http://osherove.com/blog/2007/10/8/the-various-meanings-of-tdd.html
在软件工程中,服务虚拟化是在基于异构组件(如API驱动、基于云计算的应用及SOA等)的应用中仿真具体应用行为的方法。
使用场景:
还没有完成
仍在发展
第三方或合伙人控制
测试的时间和容量有限制
很难规定或在测试环境中配置
须要由不一样的团队具备不一样的测试数据设置和其余要求的同时访问
负载和性能测试的成本很高
常见虚拟资产的建立的方法:
1,录制真实通讯
2,日志
3,分析接口规格
4,自定义行为控制接口
在SOA等体系中,使用mock工做量很大,敏捷开发加快了迭代速度,为此mock较多的状况下,一般不如采用服务虚拟化。
简单的虚拟化一般经过配置hosts文件,把目标服务器指向自定义服务器。python的flask框架由于开发速度快,常常被采用。在TCP和UDP层,python socket常用。这块不只是对单元,对接口甚至是系统等测试都意义重大。
经常使用的框架以下:
https://github.com/oarrabi/mockpy(貌似只支持MAC平台),以HTTP模拟为主。
阿里巴巴出品:https://github.com/thx/RAP 主要为帮助web工程师管理API。RAP经过GUI工具帮助WEB工程师更高效的管理接口文档,同时经过分析接口结构自动生成Mock数据、校验真实接口的正确性,使接口文档成为开发流程中的强依赖。有告终构化的API数据,RAP能够作的更多,而咱们能够避免更多重复劳动。
wiremock: http://wiremock.org/ https://github.com/tomakehurst/wiremock (推荐)
HTTP响应打桩,可匹配URL,标题和正文内容
请求验证
在单元测试运行,做为一个独立的进程或为WAR应用
Java API,JSON文件和JSON加HTTP配置
桩录制/回放
故障注入
每一个请求条件代理
浏览器代理支持请求检查和更换
状态的行为模拟
可配置的响应延迟
junitperf用于单元性能测试。
Feed4junit用于提供数据驱动。
https://github.com/jamesdbloom/mockserver 在Java, JavaScript and Ruby中mockHTTP或HTTPS。MockServer内置代理内省流量并支持端口转发、web转发等。
https://github.com/splunk/eventgen 是Splunk的事件生成器,基于Python。
https://github.com/dreamhead/moco (比较推荐,支持HTTP和socket)
@FunctionalInterface public interface Scoreable { int getScore(); }
import java.util.*; public class ScoreCollection { private List<Scoreable> scores = new ArrayList<>(); public void add(Scoreable scoreable) { scores.add(scoreable); } public int arithmeticMean() { int total = scores.stream().mapToInt(Scoreable::getScore).sum(); return total / scores.size(); } }
import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; import org.junit.*; public class ScoreCollectionTest { @Test public void answersArithmeticMeanOfTwoNumbers() { // Arrange ScoreCollection collection = new ScoreCollection(); collection.add(() -> 5); collection.add(() -> 7); // Act int actualResult = collection.arithmeticMean(); // Assert assertThat(actualResult, equalTo(6)); } }
参考资料:https://en.wikipedia.org/wiki/Service_virtualization
JUnit的4.x版使用注解来指定测试。JUnit的主页:http://junit.org/,代码托管:https://github.com/junit-team/junit。JUnit测试是类中用于测试的方法。使用注解@org.junit.Test。方法中使用的断言方法(JUnit或其余断言框架提供),检查代码的执行的实际结果与预期。
下面的代码演示JUnit测试。
建立java工程first,并在src目录下建立test目录。
建立类MyClass:
package first; public class MyClass { public int multiply(int x, int y) { // the following is just an example if (x > 999) { throw new IllegalArgumentException("X should be less than 1000"); } return x / y; } }
建立测试类MyClassTest,建立方法参见下面的安装配置部分。
package first; import static org.junit.Assert.assertEquals; import org.junit.Test; public class MyClassTest { @Test(expected = IllegalArgumentException.class) public void testExceptionIsThrown() { MyClass tester = new MyClass(); tester.multiply(1000, 5); } @Test public void testMultiply() { MyClass tester = new MyClass(); assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5)); } }
选中测试类, 右键选择Run-As → JUnit Test.
在普遍使用的Junit命名方法是类名称下测试和“测试”加Test后缀。should经常使用于测试方法名,如ordersShouldBeCreated,menuShouldGetActive,以加强可读性。Maven经过surfire插件自动生成Tests后缀的测试类。
多个测试类能够组合成test suite。运行test suite将在该suite按照指定的顺序执行全部测试类。下面演示包含两个测试类 (MyClassTest和MySecondClassTest) 的测试集,经过@Suite.SuiteClasses statement能够增长测试类。
import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ MyClassTest.class, MySecondClassTest.class }) public class AllTests { }
test suite中也能够包含test suite。
经过标准的Java代码能够在Eclipse以外运行JUnit测试。Apache Maven的或Gradle框架之类的构建框架一般与持续集成服务器(如Hudson或Jenkins)组合按期自动执行测试。
org.junit.runner.JUnitCore类的runClasses()方法容许运行测试类,返回为org.junit.runner.Result对象,包含测试结果信息。
下面类演示如何运行MyClassTest。这个类将执行测试类,输出潜在错误到控制台。
import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.notification.Failure; public class MyTestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(MyClassTest.class); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } } }
以上类能够像Java类同样从命令行运行,你只须要添加JUnit Jar到classpath便可。
Junit4.*使用注解(annotation)标识测试方法和配置测试,如下是经常使用相关annotation的简要说明。特别重要的注解以下:
注解 | 描述 |
@Test public void method() |
标识方法为测试方法 |
@Test (expected = Exception.class) | 标识抛出指定的异常 |
@Test(timeout=100) | 超时,单位ms |
@Before public void method() |
每一个测试执行以前的准备工做。用来准备测试环境,如读取输入数据,初始化类等。 |
@After public void method() |
每一个测试执行以后的清理工做。如删除临时文件,恢复初始设置,释放内存等。 |
@BeforeClass public static void method() |
每一个测试集执行以前的准备工做。用来执行费时任务,如链接数据库。必须是Static方法。 |
@AfterClass public static void method() |
每一个测试集执行以后的清理工做。用来清理,如断开数据库链接,必须是Static方法。 |
@Ignore or @Ignore("Why disabled") | 忽略指定测试方法,用于测试类还没有准备好等状况,使用时最好标明忽略的缘由。 |
@RunWith会替代默认的org.junit.runner.Runner类,好比:
@RunWith(Suite.class) public class MySuite { }
Mock也须要使用注解。
import org.junit.runner.RunWith; import org.junit.runners.Suite;
@RunWith(Suite.class) @Suite.SuiteClasses({ AssertTest.class, TestExecutionOrder.class, Assumption.class }) public class TestSuite { }
JUnit会假定全部测试方法按任意顺序执行,也就是说,一个测试不该该依赖其它测试。
Junit 4.11容许你用Annoation以字母顺序对测试方法进行排序,使用方法为用@FixMethodOrder(MethodSorters.NAME_ASCENDING)。默认使用固定但不可预期的顺序,对应参数为`MethodSorters.DEFAULT`,也可使用`MethodSorters.JVM`,表明JVM的默认方式,每次运行顺序会不一样。
自定义的实例:
package org.hamcrest.examples.tutorial; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; public class IsNotANumber extends TypeSafeMatcher<Double> { @Override public boolean matchesSafely(Double number) { return number.isNaN(); } public void describeTo(Description description) { description.appendText("not a number"); } @Factory public static <T> Matcher<Double> notANumber() { return new IsNotANumber(); } }
使用:
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.hamcrest.examples.tutorial.IsNotANumber.notANumber; import junit.framework.TestCase; public class NumberTest extends TestCase { public void testSquareRootOfMinusOneIsNotANumber() { assertThat(Math.sqrt(-1), is(notANumber())); } }
切记,要静态导入notANumber, 参考资料:https://code.google.com/p/hamcrest/wiki/Tutorial。
稍微复杂点的实例:
package com.example; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Factory; import org.hamcrest.Matcher; public class LessThanOrEqual<T extends Comparable<T>> extends BaseMatcher<Comparable<T>> { private final Comparable<T> expValue; public LessThanOrEqual(T expValue) { this.expValue= expValue; } @Override public void describeTo(Description desc) { desc.appendText(" less than or equal(<=)" +expValue); } @Override public boolean matches(Object t) { int compareTo = expValue.compareTo((T)t); return compareTo > -1; } @Factory public static<T extends Comparable<T>> Matcher<T> lessThanOrEqual(T t) { return new LessThanOrEqual(t); } }
使用:
@Test public void lessthanOrEquals_matcher() throws Exception { int actualGoalScored = 2; int expGoalScored= 4; assertThat(actualGoalScored, lessThanOrEqual(expGoalScored)); expGoalScored =2; assertThat(actualGoalScored, lessThanOrEqual(expGoalScored )); double actualDouble = 3.14; double expDouble = 9.00; assertThat(actualDouble, lessThanOrEqual(expDouble)); String authorName = "Sujoy"; String expAuthName = "Zachary"; assertThat(authorName, lessThanOrEqual(expAuthName)); }
在Gradle编译时使用Junit:
apply plugin: 'java' dependencies { testCompile 'junit:junit:4.12' }
Maven:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
Eclipse 等IDE自带Junit。
Eclipse对Junit的支持
Eclipse有建立JUnit测试的向导。例如,要为现有类建立JUnit测试或测试类,在类单击鼠标右键,New → JUnit Test Case。File → New → Other... → Java→ JUnit也可打开相似窗口。
执行:在类单击鼠标右键Run-as →JUnit Test,执行类中全部测试。Alt+Shift+X, ,T,若是光标在测试里面,只会执行当前测试。
只看失败的测试:
测试失败时才弹出:
拷贝错误信息
JUnit的静态导入
静态导入容许在类中定义的public static字段和方法不指定类就可使用。
// without static imports you have to write the following statement Assert.assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5)); // alternatively define assertEquals as static import import static org.junit.Assert.assertEquals; // more code // use assertEquals directly because of the static import assertEquals("10 x 5 must be 50", 50, tester.multiply(10, 5));
Eclipse中能够配置自动静态导入:Window → Preferences and select Java → Editor → Content Assist → Favorites.
org.junit.Assert
org.hamcrest.CoreMatchers
org.hamcrest.Matchers
这样就可使用Content Assist(快捷方式Ctrl+Space)添加方法导入。
注解:@Test (expected = Exception.class)能够测试单个异常。
测试多个异常的方法:
try { mustThrowException(); fail(); } catch (Exception e) { // expected // could also check for message of exception, etc. }
JUnit的插件测试为插件书写单元测试。这些测试运行特殊的测试执行器,在单独的虚拟机中生成Eclipse实例。
使用注解@RunWith(Parameterized.class)便可。
测试类必须包含@Parameters注解的静态方法返回数组的集合,用于做为测试方法的参数。public域使用@parameter注解能够取得测试注入测试值。
package first; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import java.util.Arrays; import java.util.Collection; import static org.junit.Assert.assertEquals; import static org.junit.runners.Parameterized.*; @RunWith(Parameterized.class) public class ParameterizedTestFields { // fields used together with @Parameter must be public @Parameter public int m1; @Parameter (value = 1) public int m2; // creates the test data @Parameters public static Collection<Object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5, 3 }, { 121, 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals("Result", m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply(int i, int j) { return i *j; } } }
使用构造方法也能够实现相似的效果:
package first; import static org.junit.Assert.assertEquals; import java.util.Arrays; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ParameterizedTestUsingConstructor { private int m1; private int m2; public ParameterizedTestUsingConstructor(int p1, int p2) { m1 = p1; m2 = p2; } // creates the test data @Parameters public static Collection<Object[]> data() { Object[][] data = new Object[][] { { 1 , 2 }, { 5, 3 }, { 121, 4 } }; return Arrays.asList(data); } @Test public void testMultiplyException() { MyClass tester = new MyClass(); assertEquals("Result", m1 * m2, tester.multiply(m1, m2)); } // class to be tested class MyClass { public int multiply(int i, int j) { return i *j; } } }
Rule能够灵活的增长或者重定义测试类每一个测试方法的行为。经过@Rule注解能够建立和配置在测试方法使用的对象。好比灵活地指定异常:
package first; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; public class RuleExceptionTesterExample { @Rule public ExpectedException exception = ExpectedException.none(); @Test public void throwsIllegalArgumentExceptionIfIconIsNull() { exception.expect(IllegalArgumentException.class); exception.expectMessage("Negative value not allowed"); ClassToBeTested t = new ClassToBeTested(); t.methodToBeTest(-1); } }
注意上述代码只作演示,不能实际执行。
JUnit的已经提供了规则的几个有用的实现。例如, TemporaryFolder类在测试执行完毕后会删除文件。
package first; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; public class RuleTester { @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { File createdFolder = folder.newFolder("newfolder"); File createdFile = folder.newFile("myfilefile.txt"); assertTrue(createdFile.exists()); } }
实现TestRule接口可自定义Rule。这个接口的apply(Statement, Description)方法返回Statement实例。Statement即JUnit运行时中的测试,Statement#evaluate()运行它们。Description 描述了单个测试。它经过反射阅读测试信息。
下例子添加日志语句到Android应用。
import android.util.Log; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; public class MyCustomRule implements TestRule { private Statement base; private Description description; @Override public Statement apply(Statement base, Description description) { this.base = base; this.description = description; return new MyStatement(base); } public class MyStatement extends Statement { private final Statement base; public MyStatement(Statement base) { this.base = base; } @Override public void evaluate() throws Throwable { Log.w("MyCustomRule",description.getMethodName() + "Started"); try { base.evaluate(); } finally { Log.w("MyCustomRule",description.getMethodName() + "Finished"); } } } }
使用Rule:
@Rule public MyCustomRule myRule = new MyCustomRule();
更多关于Rule的资料:https://github.com/junit-team/junit/wiki/Rules。
public interface FastTests { /* category marker */ } public interface SlowTests { /* category marker */ } public class A { @Test public void a() { fail(); } @Category(SlowTests.class) @Test public void b() { } } @Category({ SlowTests.class, FastTests.class }) public class B { @Test public void c() { } } @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b and B.c, but not A.a } @RunWith(Categories.class) @IncludeCategory(SlowTests.class) @ExcludeCategory(FastTests.class) @SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite public class SlowTestSuite { // Will run A.b, but not A.a or B.c }
本部分参考资料:https://github.com/junit-team/junit/blob/master/doc/ReleaseNotes4.8.md
标准断言:
JUnit的Assert类提供一些静态方法,通常以assert开头,参数分别为错误信息,指望值,实际值。若是指望与实际不符,将抛出AssertionException异常。
如下给出这类方法的概览,[]中的参数为可选,类型为String。
语句 | 描述 |
assertTrue([message,] boolean condition) | 确认布尔条件为真。 |
assertFalse([message,] boolean condition) | 确认布尔条件为假 |
assertEquals([message,] expected, actual) | 确认返回等于指望,数组等比较的是地址而非内容。 |
assertEquals([message,] expected, actual, tolerance) | 测试float或double相等。 tolerance为允许的偏差。 |
assertNull([message,] object) | 对象为null |
assertNotNull([message,] object) | 对象非null |
assertSame([message,] expected, actual) | 变量指向同一对象. |
assertNotSame([message,] expected, actual) | 变量指向不一样对象 |
assertEquals(double expected,double actual, double delta)能够解决double的精度偏差,对于钱,建议使用BigDecimal类型。另外还有个特殊的fail(message)。
Hamcrest对断言进行了扩展
assertThat扩展了断言,语法以下:
public static void assertThat(Object actual, Matcher matcher)。
导入import static org.hamcrest.CoreMatchers.*;
Matcher实现了org.hamcrest.Matcher接口,而且能够组合,好比:
• assertThat(calculatedTax, is(not(thirtyPercent)) );
• assertThat(phdStudentList, hasItem(DrJohn) );
• assertThat(manchesterUnitedClub, both( is(EPL_Champion)).and(is(UEFA_Champions_League_Champion)) );
Matcher列表以下:
allOf , anyOf , both , either , describedAs , everyItem , is , isA , anything ,
hasItem , hasItems , equalTo , any , instanceOf , not , nullValue , notNullValue ,
sameInstance , theInstance , startsWith , endsWith , and containsString。
import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; import java.util.Arrays; import java.util.List; import org.junit.Test; public class AssertThatTest { @Test public void test_matcher_behavior() throws Exception { int myAge = 30; //examine the exact match with equalTo and is assertThat(myAge, equalTo(30)); assertThat(myAge, is(30)); //examine partial match with not() assertThat(myAge, not(equalTo(33))); assertThat(myAge, is(not(33))); } @Test public void verify_multiple_values() throws Exception { double myMarks = 100.00; assertThat(myMarks, either(is(100.00)).or(is(90.9))); assertThat(myMarks, both(not(99.99)).and(not(60.00))); assertThat(myMarks, anyOf(is(100.00),is(1.00),is(55.00),is(88.00),is(67.8))); assertThat(myMarks, not(anyOf(is(0.00),is(200.00)))); assertThat(myMarks, not(allOf(is(1.00),is(100.00),is(30.00)))); } @Test public void verify_collection_values() throws Exception { List<Double> salary =Arrays.asList(50.0, 200.0, 500.0); assertThat(salary, hasItem(50.00)); assertThat(salary, hasItems(50.00, 200.00)); assertThat(salary, not(hasItem(1.00))); } @Test public void verify_Strings() throws Exception { String myName = "John Jr Dale"; assertThat(myName, startsWith("John")); assertThat(myName, endsWith("Dale")); assertThat(myName, containsString("Jr")); } }
Pragmatic Unit Testing in Java 8 with JUnit -2015 英文 pdf
Pragmatic Unit Testing in Java with JUnit -2004 英文 pdf -- 中文翻译 《单元测试之道Java版》 扫描版