Interface: Test 整个测试的的基础接口
Method 1: abstract int countTestCases() 这个方法主要是用来计算要运行的test case的数量的。
Method 2:abstract void run(TestResult result) 这个方法主要是用来执行一个测试用例并且在测试结果的实例中收集它的测试结果。
Class:TestCase
定义:abstract class TestCase extends Assert implements Test 继承了Assert类并且实现了Test接口。而且是个抽象类。
Class: TestSuite
定义:class TestSuite implements Test; Test接口的实现类,和TestCase一样. 但是TestSuite是一系列testcase的集合,将所有要运行的testcase add到TestSuite。
观察一:TestCase 和 TestSuite类都实现了Test 接口。
好处一:由于当你给TestSuite增加一个对象时,实际上增加的是Test,而不只是个TestCase,这样我们就既可以在TestSuite里面增加另一个TestSuite,也可以加入一个TestCase。如果是TestCase,那么就会运行那个单独的测试;如果是TestSuite,那么就运行一组测试。
如何实现:那么是如何实现上面说的那个好处呢?看下TestSuite的一个addtest方法就明白了:
Public void addTest(Test test) 新增一个test到testsuite,直接使用fTests.add(test)来新增。
这里可以新增的入参是Test接口,而不是一个TestCase对象,所以只要实现了Test接口的实现类,包括TestCase和TestSuite,都可以不断的增加下去。这样就引出了好处二和设计模式。
好处二:当我们为自己的应用程序创建特殊的suite或组合出TestAll类非常容易。简单说明下TestAll类:仅仅包含了一个静态的suite方法,会注册需要定期执行的所有的Test对象(包括TestCase对象和TestSuite对象)。
Java设计模式:Composite模式
定义:把对象组合(composite)成树状结构来表示部分-整体层次关系,Composite模式可以让客户一致的对待单个对象和对象的组合。
我们来看下Junit的Test接口和TestCase和TestSuite类是如何实现Composite模式的:
先搞清楚Junit里面的部分-整体分别是什么,这里单个的TestCase可以看作是部分,把复合的TestCase(TestSuite)看作是整体,看下面的图:
我们可以看到这样的模式会带来另外一些好处:
简化了JUnit的代码 JUnit可以统一处理组合结构TestSuite和单个对象TestCase。使JUnit开发变得简单容易,因为不需要区分部分和整体的区别,不需要写一些充斥着if else的选择语句。
好处三:TestCase是个抽象实现类,而且继承了Assert类,这里为啥要继承Assert类呢?因为在一个testcase中是肯定需要用的Assert断言的,如果不继承Assert类,那么testcase方法中要写成Assert.assert(),不是很简洁,现在继承了Assert类后,我们就可以在继承了TestCase类的测试类中直接assert(),使测试代码看得非常简洁和清楚。
定义了TestCase对象和TestSuite的类层次结构 基本对象TestCase可以被组合成更复杂的组合对象TestSuite,而这些组合对象又可以被组合,如我们上个例子,这样不断地递归下去。客户代码中,任何使用基本对象的地方都方便的使用组合对象,大大简化系统维护和开发。
仔细看看Test接口的方法,它存在一个是countTestCases方法,它来统计这次测试有多少个TestCase,另外一个方法run。还有一个参数TestResult,它来统计测试结果。这里为啥存在一个run方法呢? 我们想想Junit在run testcase的时候,需要把这些testcase的组成打包后成为请求发送到Junit Framework,这样我们实现TestCase类的时候,可以自由的实现run方法去打包发送请求。这样我们在写测试用例时候,只需继承TestCase,来完成run方法即可,把测试结果记录到TestResult中,这样的做法就好引出另外的Java设计模式。
Java设计模式:Command模式
定义:将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化;对请求进行排队或记录请求日志...,Command告诉我们可以为一个操作生成一个对象并给出它的一个execute(执行)方法。
run()就是我们的Command模式的Excecute方法:
我们可以看到这样的模式会带来另外一些好处:
Command模式将实现请求的一方(TestCase开发)和调用一方(JUnit Fromwork)分离开。
Command模式可以将多个TestCase进行组合成一个复合命令,实际你将看到TestSuit就是它的复合命令。
之前我们看到了Test接口里面的run方法有个TestResult的参数,不错,这个类就是用来收集测试结果的,是收集TestSuite的运行结果,所以一般情况下,一个TestSuite对应一个TestResult. TestResult存储了所有测试的详细情况,是通过还是失败。
如果是失败:Junit会创建一个TestFailure对象,并保存在TestResult中。
这里知道了TestResult的作用,这种做法也会引入另外一个设计模式。
Java设计模式:Collecting Parameter模式
定义:当你需要从几个方法中收集结果时,你应当给方法增加一个参数,并传递一个会替你收集参数的对象。
这里TestResult类就是起到了这个作用。但是我们知道TestResult是收集很多运行的Test的运行结果,这里就需要对于这些运行结果进行管理,则TestResult类定义了如下相关的方法:
public synchronized void addError(Test test, Throwable t) 新增一个错误到ArrayList<TestFailure>。
public synchronized void addFailure(Test test, AssertionFailedError t) 新增一个失败到ArrayList<TestFailure>。
public synchronized void addListener(TestListener listener) 在一个test中注册一个监听器到ArrayList<TestListener>,这个监听器就是TestListener,实现类是TestRunner。
public synchronized void removeListener(TestListener listener) 从一个test中取消这个监听器。
private synchronized List<TestListener> cloneListeners() 克隆一批监听器。
我们已经知道了TestResult的作用,那么TestListener的作用又是什么呢?在Run一个测试用例的时候有很多的结果,这时就由TestListener去观察这个运行的结果,并且负责报告这些运行信息。
TestListener是个接口,一般由Test Runner,很多特定的Junit扩展也实现了这个接口,我们来看下这个接口里面定义了哪些方法:
public void addError(Test test, Throwable t); 发送错误的时候才被调用。
public void addFailure(Test test, AssertionFailedError t); 失败的时候才被调用。
public void endTest(Test test); 测试结束时被调用。
public void startTest(Test test); 测试开始时被调用。
由于定义了这个TestListener接口和实现类TestRunner的作用都看到了,特别是给扩展Junit提供了新的实现类的方式,这样的做法就引出了一个设计模式。
Java设计模式:Observer模式
定义:在对象之间定义了一个一对多的依赖关系,这样当一个对象改变了状态,那么所有依赖于它的对象都会自动收到通知且更新。目前Junit框架的TestRunner就以TestListener的身份注册到TestResult。
我们在写testcase的时候,都会用到Assert方法去check运行结果,这时候的Assert方法是继承了Junit的TestCase类,但是你如果还记得TestCase类的声明的话,那就是TestCase不仅仅实现了Test接口,而且也继承了Assert类,其实这些Assert方法就是Assert类中实现的。
Junit的Assert类中总共有38个Assert方法,但很多都是不停的重载,其实就只有8个核心方法:
assertTure; assertFalse; assertEquals; assertNotEquals; assertNull; assertSame; assertNotSame; fail(让测试失败,并给出指定的信息)
一般要用到抛出message的都会用到fail方法。
我们以一个非常简单的TestCalculator类为例,只有一个测试方法:
Public class TestCalculator extends TestCase
{
Public void testAdd()
{
Calculator calculator = new Calculator();
Double result = calculator.add(10, 50);
assertEquals(60, result);
}
}
当我们使用Run in Junit 的时候,之前说到过的几个核心类之间是怎么运行的呢?我们先看下基本过程:
TestRunner启动界面框 —》创建一个TestSuite ––》创建一个TestResult—》执行testadd方法。
由于我们的TestCalculator测试类里面没有显式的suite方法,大部分情况下都是类似,这样的话,TestRunner创建了一个默认的TestSuite对象,看下图:
那么同时TestRunner还需要创建包含测试结果(成功,失败或出错)的TestResult对象,具体过程如下:
1.最开始的时候,TestRunner实例化了一个TestResult对象,在测试顺序执行的时候,这个对象将用于保存测试结果。
2.TestRunner向TestResult注册,就是add一个监听器,这样的话在执行测试过程中,TestRunner就可以收到各种事件,TestResult会广播如下方法:测试开始(startTest); 测试失败(addFailure); 遇到测试错误(addError); 测试结束(endTest)
3.知道了这些事件后,TestRunner就可以随着测试的进行而显示进度条了,并且在失败或错误的时候显示出来。
4.TestRunner通过调用TestSuite的run(TestResult)方法来开始测试
5.TestSuite为它所拥有的每个Test Case实例调用run(TestResult)方法。
6.Test Case使用传递给它的TestResult实例来调用其run(Test)方法,并把自身作为参数传递给run方法,这样TestResult 立马就可以用runBare()来回调它。
接着就是执行测试方法了:
这里需要说明的是对于每个TestCase都会调用runBare()方法,这里只有一个testAdd方法,所以只调用一次,请看下图:
1.runBare()方法将调用setUp, testAdd, teardown 方法,顺序执行。
2.如果调用3个方法的过程出现任何失败或错误,那么TestResult就会分别调用addFailure 和addError来通知它的所有Listener。
3.这样TestRunner就会收到这些错误或失败,也会罗列出这些错误,否则进度条就是绿色的,让你知道测试方法没有问题。
4.当tearDown方法执行完后,测试也就完成了,TestResult 会通过调用endTest把这个结果通告给所有的Listener。
我们做使用Junit工具来做单页测试或接口测试时,需要注意一些问题,包括我们的编码规范,test规范,以及编写测试代码的策略,以下个人的总结。
1.为还没有实现的测试代码抛出一个异常。这就避免了该测试通过,而且会提醒你必须实现其代码。
2.一次只测试一个对象。单元测试一个重要的方面就是细粒度,它独立的检查你创建的每个对象,这样你就可以在问题发生的第一时间就把它们隔离起来。如果测试多于一个对象,那么你就无法预测到这些对象发生了改变时它们会如何相互影响的。
3.选择有意义的测试方法名。你应当能通过阅读方法名就可以理解要测试的是什么方法。一条好的规则就是一开始就遵守test_XX1_XX2的命名模式,其中XX1是待测方法名,XX2就是测试条件或目的。
4.在assert调用中解释失败原因。无论何时,只要你用到assertTrue,assertFalse,assertNull,assertNotNull方法,请记住要使用第一个参数是String类型的那个方法,这个参数让你可以提供一个有意义的文本描述,在断言失败的时候,Junit TestRunner 会显示这个描述,若不使用这个参数,那么当测试失败时就比较难找出失败原因了。
5.测试任何可能失败的事物。测试主执行路径很好,而且很需要做;但测试异常处理可能更重要。如果主执行路径出错,那么可能应用程序也无法工作。
6.让测试改善代码。编写单元测试常常有助于你写成更好的代码。理由很简单,testcase是你的代码的用户,只有在使用代码的时候才能发现代码的缺点,所以应当根据测试时发现的不便之处重构代码,使其更易于使用。TDD就类似,通过先编写测试,再从代码用户的角度来开发你的类。
7.让异常测试易读。把catch块中的异常变量命名为expected,这样就可以明确的告知读代码的人,这个异常是我们预期的,抛出异常才能让测试通过,在catch块中加入assertTrue(true)语句也进一步强调,这才是正确的执行路径。
8.同一个包,分离的目录。把所有测试类和待测类都放在同一个包中,但使用平行目录结构,为了可以访问保护型的方法,需要把测试和待测类放在同一个包中,为了简化文件管理,并让人清晰的分辨测试类和开发代码类,所有需要把测试放在一个单独的目录中。当然我们也可以使用一个test的包,进行遗漏所有开发包。这样也可以把test需要的resources文件也可也独立起来。
9.存在三种单元测试:逻辑单元测试,集成单元测试,功能单元测试。逻辑单元测试主要是检查代码逻辑性,通常只是针对单个方法,一般可以通过mock objects 和stub 来控制特定的方法的边界。 集成单元测试主要是在真实环境下的一个组件相互交互的测试。功能单元测试目的是为了确认响应的结果是否正确,这种单元测试更多的依赖于外部环境。
10.考察单元测试的覆盖率。一种方法就是数一下你的测试中用到了多少方法。使用Clover工具进行覆盖率统计,记住100%的测试覆盖并不能保证你的应用程序得到了100%的测试。
11.测试先行。在写任何代码之前,必须先写一个失败的测试。为啥是失败的测试呢,因为不是失败的测试,老是成功的测试,你就不会改进这些代码来使其更加具有维护性。
相关推荐
4. 单元测试:通过JUnit或Espresso等测试框架编写,验证加密和解密过程的正确性。 单元测试在软件开发中扮演着重要角色,特别是在安全相关的功能上。对于加密模块,测试可能包括: - 测试加密过程:确保输入明文...
8. **测试框架**:有效的测试源码通常基于一定的测试框架,例如JUnit或CTest,这些框架提供了自动化测试、结果记录和异常处理等功能。 9. **适配层**:加密卡与主机之间的通信通常需要适配层,测试源码会涉及这一层...
项目提供了源码,意味着可以深入学习其内部实现,包括设计模式、类结构、方法逻辑等。这对于理解Java编程、提升编程技巧和问题解决能力大有裨益。 【工具】: 1. **开发工具**:可能使用IDEA、Eclipse等Java集成...
- **javap**:Java反汇编器,用于查看类文件的内部结构。 - **javaw**:无窗口模式启动Java应用程序。 - **native2ascii**:转换本地编码的文本文件为Unicode编码。 - **serialver**:确定类的序列版本号。 这些...
企业门户是整合企业内部各种信息资源、提供个性化服务和工作流程集成的平台,它能提高工作效率,优化业务流程,使财务人员能够更加便捷地访问和处理数据。 在门户开发过程中,"源码"是核心组成部分,意味着我们需要...
6. **内部类与接口**:内部类允许在一个类的内部定义另一个类,接口则用于定义一组方法,实现接口的类必须实现接口中所有方法。 7. **异常与错误**:Error和Exception是Java中异常处理的两个主要类别,Error通常...
3. **加密与解密**:SSH使用各种加密算法来确保数据安全,理解这些算法的工作原理至关重要,例如非对称加密和对称加密。 4. **Eclipse IDE使用**:熟悉Eclipse的界面和功能,包括创建项目、导入源代码、设置构建...
4. 熟悉Android四大组件(Activity、Service、BroadcastReceiver、ContentProvider)的生命周期及编程应用,掌握五大存储方式(SharedPreference、SQLite、文件、内部存储、外部存储)。 5. 熟练运用Android平台下的...
1. **源代码文件**:这部分可能包括用各种编程语言(如Java、Python、C++或C#)编写的源代码,负责实现加密和解密算法。常见的文件可能有`Encryptor.java`、`Decryptor.py`等,这些文件会实现如AES(高级加密标准)...
- **Intent机制**:如何使用Intent在不同组件间传递数据和启动服务,实现应用内部或跨应用通信。 - **布局设计**:XML布局文件的使用,包括LinearLayout、RelativeLayout、ConstraintLayout等,用于构建用户界面。...
10. DSL(领域特定语言):Kotlin允许构建内部DSL,这可能在配置"信封工具"的行为或规则时派上用场。 通过对`EnvelopeTool-main`文件夹中的源代码进行深入研究,我们可以了解更多关于这个工具的具体实现细节,包括...
在深入项目之前,首先需要解压`aesook-master`文件,查看其内部结构,如`README.md`文件以获取项目说明,`pom.xml`或`build.gradle`文件来了解构建配置,以及`src/main/java`目录下的源代码。然后,通过阅读代码、...
完成Caesar Cipher的Java程序后,可以进行单元测试,确保对各种输入(包括不同位移量、包含非字母字符的字符串、大小写字母等)都能正确加密和解密。这可能需要使用JUnit等测试框架来编写测试用例。 总的来说,...
通常,这样的文件夹会包含一个或多个子文件夹,每个子文件夹对应一个具体的 LeetCode 题目,内部有对应的 Java 解决方案源代码文件,如 "Problem1_Solution.java","Problem2_Solution.java" 等。这些文件会展示如何...
单元测试可以通过JUnit框架配合Android Test框架实现。 #### 40. 代码获取应用程序的名称,包名,版本号和图标 可以通过`PackageManager`类获取应用的相关信息。 #### 41. 卸载程序 卸载程序可以通过创建一个...
- **Java集合框架:** List、Set、Map等集合类的原理和使用场景,以及它们的子类如ArrayList、HashMap的内部结构和性能差异。 - **Java I/O操作:** 输入输出流的处理、NIO和IO的区别以及异步I/O的使用。 - **多线程...
类之间的关系可能是继承(继承自一个父类或接口)、封装(隐藏内部实现细节)和多态(不同类的对象可以响应相同的消息)。熟悉这些概念有助于编写可维护和可扩展的代码。 此外,文件操作也是Java编程中的常见任务。...
- **定义**: 重构是在不改变外部行为的前提下,改进软件内部结构的过程。 - **测试支持**: 在重构之前编写充分的测试用例,确保重构过程中不会破坏原有功能。 #### 三、测试先行的应用开发 **3.1 测试框架** - **...
- **表示层**:数据加密解密。 - **应用层**:提供应用程序接口。 ##### 线程和进程的区别 - **进程**:操作系统分配资源的基本单位,每个进程有自己的内存空间。 - **线程**:进程内的执行单元,共享进程资源,...