JUnit A Cook's Tour
Note:this article is based on JUnit 3.8.x.
1.序言
在一篇早期的文章中(见Test Infected: Programmers Love Writing Tests, Java Report, July 1998, Volume 3, Number 7),我们描述了如何使用一个简单框架去编写可重复的测试。在本文,我们将揭开框架的面纱告诉你它是如何构成的。
我们小心翼翼研究了JUnit框架并且思考我们是如何构建它。我们找到很多不同难度的课程。在本文我们试着把他们都串起来写,发现这很难实现,但至少我们将向你展示这个软件中有价值的设计。
我们以框架的目标作为讨论的起点。这些目标将多次在框架描述的细节中重现,从中我们慢慢呈现这个框架的设计和实现。我们以多种形式的设计模式来描述它的设计(很惊讶吧),它的实现就像一个内容丰富的程序。我们以一些关于框架发展的想法作为本文的结束。
2.目标
JUnit的目标是什么?
首先,我们重新回到开发的假设情况。如果一个程序缺少自动测试,我们假定它不能运行。这个假设看来比那个盛行的假设更安全一些,那个假设说的是,如果一个开发者向我们保证,一个程序能运行,那么它现在并且永远能运行下去。
根据这个看法,开发者编写和调试代码时他们都没能做到,他们也必须编写测试以保证程序能运行。但不管怎样,每个人都很忙,他们要做的事情太多,他们不能为测试挤出足够的时间。我已经有太多的代码要写了,为何还要写测试代码呢?繁忙的项目经理这样回答我。
因此,我们第一个目标就是编写一个我们能看到希望火光的、能让开发者们真正去编写测试的框架。这个框架必须使用大家熟悉的工具,以致不用学什么新东西。它不需要多余的工作去编写一个新的测试。它还必须能消费重复。
如果你不得不去做所有的测试,你可能会使用调试器来调试语句。然而,这样的测试是不充分的。你的程序现在能运行,对于我来说并没有帮助,因为它不能让我确信,当我集成代码后它还能继续运行。它也不能让我确信,在你走后的未来五年它依然能运行。
因此,测试框架的第二个目标是创建的测试能长期保证它们的作用。除原始作者外的其他人,也必须能够去执行测试并理解测试结果。它可以联合各个作者的测试,并且一起运行它们时不用担心受到干扰。
最后,它必须能在创建新的测试时利用好已存在的测试代码。进行系统初始化的代价是昂贵的,这个框架必须能重新利用系统初始化去运行不同的测试。噢,这些还不够吗?
3.JUnit的设计
JUnit的设计将以一种首次使用的样式来展现(见"Patterns Generate Architectures", Kent Beck and Ralph Johnson, ECOOP 94)。这样做是为了说明一个系统的设计,开始时是没有模式的,接着运用一个又一个的模式直到系统的架构出来。我们将列出构建时解决的问题、总结实现的模式和展示这些模式是如何在JUnit中运用的。
3.1 开始-TestCase
首先,我们必须制造一个对象来表现TestCase这个基本概念。开发者通常都有测试用例的概念,但理解它们的方式有以下几种:
打印状态语句,调试器的调试语句和测试脚本。
如果想要很容易地操作测试代码,我们必须让它们变成对象。这样做可以实现一个目标,就是创建能长期保证它们作用的测试。同时,开发者经常会使用对象。把测试做成对象的决定,是因为能实现让编写测试更有吸引力(至少没有强迫性)这个目标。命令模式(见Gamma, E., et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995)能很漂亮地满足我们的要求。根据这个意图引用,“把请求封装成对象,从而让你...”命令模式告诉我们,为操作创建一个对象并且赋予它一个执行的方法。TestCase类定义的代码为:
public abstract class TestCase implements Test {
…
}
因为希望通过继承来重用这个类,我们申明它为"public abstract"。当前,我们忽略它实现了Test接口这个事实。因此,你可以认为TestCase是单独的类。
每一个TestCase创建时附带一个名称,那么如果测试失败了,你能识别出哪个测试是失败的。
public abstract class TestCase implements Test {
private final String fName;
public TestCase(String name) {
fName= name;
}
public abstract void run();
…
}
图1是TestCase的类图:
图1 使用命令模式的TestCase
3.2 实现-run()
下一个需要解决的问题是,为开发者放置初始化和测试代码找一个合适的地方。把TestCase申明为abstract是希望开发者能通过子类来重用TeseCase。然而,如果只是提供只有一个变量没有任何行为的超类,我们将不能实现首要目标,使测试更容易编写。
幸运地的是,我们为所有的测试提供了一个公用的结构:建立测试装置(fixture),在装置的基础上执行代码,校验结果和清除装置。这意味着,每一个测试都使用新的装置来执行,并且它的结果不会影响到另一个测试的结果。这样能满足最大化测试的价值的目标。
模板方法模式能很好地解决我们的问题。根据这个意图引用,“在一个操作中定义算法的骨架,延迟这些步骤在子类中实现。模板方法模式可以让子类重新定义算法中的某些步骤,而无需改变算法的结构。”这非常合适。我们希望开发者能够分开地思考,如何编写装置(set up and tear down)代码,如何编写测试代码。算法执行的顺序,对所有测试来说都是一样的,无论装置代码如何编写或者测试代码如何编写。
模板方法如下:
public void run() {
setUp();
runTest();
tearDown();
}
这些方法的默认实现是空的:
protected void runTest() {
}
protected void setUp() {
}
protected void tearDown() {
}
由于setUp和tearDown可以被覆盖,并且只能让框架去调用,所以我们申明它们为protected。第二个场景在图2中描述:
图2 使用模板方法模式的TestCase.run()
3.3 记录结果-TestResult
在错综复杂的测试中执行一个TestCase,有谁还会关心它的结果?当然,你执行测试需要确保它们跑起来。当测试结束时,你需要的只是一个记录,能和不能运行的总结。
如果测试有同等机率的成功和失败,或者只跑一个测试,我们可以在TestCase对象中设置一个标志,并且当测试完成时去查看这个标志。然而,测试是很不对称的-它们通常能运行。因此,我们需要去记录失败的问题和成功的摘要。
Smalltalk最佳实践模式(见Beck, K. Smalltalk Best Practice Patterns, Prentice Hall, 1996)中有一个模式比较适用。它叫参数收集。它建议的是,当你需要在多个方法中收集结果时,你可以传给方法一个参数或者对象,用这个对象去收集这些方法的执行结果。我们创建一个新的Object,TestResult,去收集测试执行的结果。
public class TestResult extends Object {
protected int fRunTests;
public TestResult() {
fRunTests= 0;
}
}
这个TestResult的简单版本只能对执行的测试进行计数。为了使用它,我们需要给TestCase.run()方法传一个参数,通知TestResult测试正在执行:
public void run(TestResult result) {
result.startTest(this);
setUp();
runTest();
tearDown();
}
TestResult需要保持测试数目的状态:
public synchronized void startTest(Test test) {
fRunTests++;
}
我们定义TesetResult的方法startTest是同步的,是为了当测试在不同线程上执行时,TestResult单例能够安全地收集执行结果。最后,我们想保留TestCase简单的对外接口,因此创建一个无参的run()来返回TestResult:
public TestResult run() {
TestResult result= createResult();
run(result);
return result;
}
protected TestResult createResult() {
return new TestResult();
}
图3展示了下一个设计模式。
图3 使用参数收集模式的TestResult
如果测试总是执行成功,我们就没必要编写它们了。测试失败会引人注意,特别是我们并不希望它们失败。此外,测试可以以我们期望的方式来失败,比如计算一个不正确的值,或者以一些明显的方式来失败,比如编写越界的数组。不管怎样即使测试失败了,我们也要继续执行其他的测试。
JUnit区分失败和错误。失败的可能性是可预料的,可用断言来检查。错误是不可预料的问题,比如ArrayIndexOutOfBoundsException。失败以AssertionFailedError为标志。为了使不可预料的错误区别于失败,我们用一个额外的语句(语句1)来捕抓失败。语句2捕抓其他的异常,确保测试能进行下去。
public void run(TestResult result) {
result.startTest(this);
setUp();
try {
runTest();
}
catch (AssertionFailedError e) { //1
result.addFailure(this, e);
}
catch (Throwable e) { // 2
result.addError(this, e);
}
finally {
tearDown();
}
}
通过TestCase提供的断言方法可触发AssertionFailedError。JUnit提供了一组断言方法用于不同的目的。以下是最简单的一个:
protected void assertTrue(boolean condition) {
if (!condition)
throw new AssertionFailedError();
}
这不意味着由客户端(TestCase中的一个测试方法)来捕抓AssertionFailedError,而是由模板方法TestCase.run()来处理。因此我们从Error引申出AssertionFailedError。
public class AssertionFailedError extends Error {
public AssertionFailedError () {}
}
在TestResult中收集错误的方法如下:
public synchronized void addError(Test test, Throwable t) {
fErrors.addElement(new TestFailure(test, t));
}
public synchronized void addFailure(Test test, Throwable t) {
fFailures.addElement(new TestFailure(test, t));
}
TestFailure是一个小型的框架内部辅助类,用来绑定失败的测试和稍后报告的异常。
public class TestFailure extends Object {
protected Test fFailedTest;
protected Throwable fThrownException;
}
参数收集的权威形式要求我们给每个方法传递收集对象。如果我们按照这个建议,为了收集TestResult,每一个测试方法都需要传递一个参数。这造成了对方法签名的“污染(pollution)”。我们可以避免这种签名污染。测试用例方法可以抛出异常而不需要知道TestResult。以下是更新了的MoneyTest套件中的一个测试方法。它说明了测试方法如何不需要知道任何与TestResult有关:
public void testMoneyEquals() {
assertTrue(!f12CHF.equals(null));
assertEquals(f12CHF, f12CHF);
assertEquals(f12CHF, new Money(12, "CHF"));
assertTrue(!f12CHF.equals(f14CHF));
}
JUnit配置了TestResult的不同实现。默认的实现可以对失败和错误的测试进行计数,并且收集执行的结果。TextTestResult收集结果并以文本方式显示它们。最后,UITestResult作为JUnit Test Runner的图形化版本,来增强测试状态的图形化。
TestResult是框架的一个扩展点。客户端可以定义自己定制的TestResult类,比如,HTMLTestResult以HTML文档的形式来显示结果。
分享到:
相关推荐
哈希表源码
sun_3ck_03_0119
内容概要:本文档详细介绍了基于 MATLAB 实现的 LSTM-AdaBoost 时间序列预测模型,涵盖项目背景、目标、挑战、特点、应用领域以及模型架构和代码示例。随着大数据和AI的发展,时间序列预测变得至关重要。传统方法如 ARIMA 在复杂非线性序列中表现欠佳,因此引入了 LSTM 来捕捉长期依赖性。但 LSTM 存在易陷局部最优、对噪声鲁棒性差的问题,故加入 AdaBoost 提高模型准确性和鲁棒性。两者结合能更好应对非线性和长期依赖的数据,提供更稳定的预测。项目还展示了如何在 MATLAB 中具体实现模型的各个环节。 适用人群:对时间序列预测感兴趣的开发者、研究人员及学生,特别是有一定 MATLAB 编程经验和熟悉深度学习或机器学习基础知识的人群。 使用场景及目标:①适用于金融市场价格预测、气象预报、工业生产故障检测等多种需要时间序列分析的场合;②帮助使用者理解并掌握将LSTM与AdaBoost结合的实现细节及其在提高预测精度和抗噪方面的优势。 其他说明:尽管该模型有诸多优点,但仍存在训练时间长、计算成本高等挑战。文中提及通过优化数据预处理、调整超参数等方式改进性能。同时给出了完整的MATLAB代码实现,便于学习与复现。
1996-2019年各地级市平均工资数据 1、时间:1996-2019年 2、来源:城市nj、各地级市统计j 3、指标:平均工资(在岗职工) 4、范围:295个地级市
AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!
内容概要:本文介绍了一种新颖的变压器模型C2Former(Calibrated and Complementary Transformer),专门用于解决RGB图像和红外图像之间的物体检测难题。传统方法在进行多模态融合时面临两个主要问题——模态错位(Modality miscalibration)和融合不准确(fusion imprecision)。作者针对这两个问题提出采用互模交叉注意力模块(Inter-modality Cross-Attention, ICA)以及自适应特征采样模块(Adaptive Feature Sampling, AFS)来改善。具体来说,ICA可以获取对齐并且互补的特性,在特征层面进行更好的整合;而AFS则减少了计算成本。通过实验验证了基于C2Former的一阶段和二阶段检测器均能在现有公开数据集上达到最先进的表现。 适合人群:计算机视觉领域的研究人员和技术人员,特别是从事跨模态目标检测的研究人员,对Transformer架构有一定了解的开发者。 使用场景及目标:适用于需要将可见光和热成像传感器相结合的应用场合,例如全天候的视频监控系统、无人驾驶汽车、无人
上海人工智能实验室:金融大模型应用评测报告-摘要版2024.pdf
malpass_02_0907
C++-自制学习辅助工具
内容概要:本文提供了有关微信生态系统的综合开发指导,具体涵盖了微信机器人的Java与Python开发、全套及特定应用的小程序源码(PHP后台、DeepSeek集成),以及微信公众号的基础开发与智能集成方法。文中不仅给出了各种应用的具体案例和技术要点如图灵API对接、DeepSeek大模型接入等的简述,还指出了相关资源链接以便深度探究或直接获取源码进行开发。 适合人群:有意开发微信应用程序或提升相应技能的技术爱好者和专业人士。不论是初涉者寻求基本理解和操作流程,还是进阶者期望利用提供的资源进行项目构建或是研究。 使用场景及目标:开发者能够根据自身兴趣选择不同方向深入学习微信平台的应用创建,如社交自动化(机器人)、移动互联网服务交付(小程序),或者公众信息服务(公众号)。特别是想要尝试引入AI能力到应用中的人士,文中介绍的内容非常有价值。 其他说明:文中提及的多个项目都涉及到了最新技术栈(如DeepSeek大模型),并且为不同层次的学习者提供从零开始的详细资料。对于那些想要迅速获得成果同时深入了解背后原理的人来说是个很好的起点。
pimpinella_3cd_01_0916
mellitz_3cd_01_0516
schube_3cd_01_0118
AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!
AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!
AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!
智慧用电平台建设解决方案【28页】
lusted_3ck_01_0519
HCIP作业1 这里面是完成的ensp的拓扑图
会员式点餐小程序1.2.1 前端 会员卡点餐小程序 适用于书吧、咖啡书屋、健身房等有会员卡充值需求的场所。 小程序专属会员模式,可享受折扣为余额充值,稳定客流。 版本号:1.2.1 适配一个php兼容性错误 修改消息通知模板