问题
如果测试总是能够正确运行,那么我们将没有必要编写它们。只有当测试失败时测试才是有意义的,尤其是当我们没有预期到它们会失败的时候。更有甚者,测试能够以我们所预期的方式失败。JUnit区分了失败(failures)和错误(errors)。失败的可能性是可预期的,
Junit 中的设计模式
并且以使用断言(assertion)来进行检查。而错误则是不可预期的问题,如ArrayIndexOutOfBoundsException。因此我们必须进行报告测试的进行状况,或者打印到控制台,或者是文件,或者GUI界面,甚至同时需要输出到多种介质。如JUnit提供了三种方式如Text,AWT,Swing这三种运行方式,并且JUnit需要提供方便的扩展接口,这样就存在对象间的依赖关系,当测试进行时的状态发生时(TestCase的执行有错误或者失败等),所有依赖这些状态的对象必须自动更新,但是JUnit又不希望为了维护一致性而使各个类紧密耦合,因为这样会降低它们的重用性,怎样解却这个问题?
模式的选择
同样需要思考设计模式的适用性,Observer(观察者)模式便是第一个要考虑的。Observer观察者模式是行为模式,又叫做发布-订阅(Publish-Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式。具有以下意图“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。这听起来非常适合需求。在JUnit测试用例时,测试信息一旦发生改变,如发生错误或者失败,结束测试等,各种输出就要有相应的更新,如文本输出就要在控制台打印信息,GUI则在图形中标记错误信息等。
Observer(观察者)模式的构成
1、Subject 提供注册和删除观察者对象的方法,可以保存多个观察者
2、 ConcreteSubject 当它的状态发生改变时,向它的各个观察者发出通知
3、 Observer 定义那些目标发生改变时需要获得通知的对象一个更新接口
4、 ConcreteObserver 实现更新接口
Observer模式的代码实现:
Subject:
public interface Subject {
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
public List<Observer> getAll();
public void notifyAllObservers();
}
ConcreteSubject:
public class ConcreteSubject implements Subject {
private List<Observer> list = new ArrayList<Observer>();
public void addObserver(Observer observer) {
list.add(observer);
}
public List<Observer> getAll() {
return list;
}
public void notifyAllObservers() {
for (Observer observer : list) {
observer.update();
}
}
public void removeObserver(Observer observer) {
list.remove(observer);
}
}
Observer:
public interface Observer {
public void update();
}
ConcreteObserver:
public class ConcreteObserver implements Observer {
public void update() {
System.out.println("update");
}
}
Client:
public class Client {
public static void main(String[] args) {
Observer o1 = new ConcreteObserver();
Observer o2 = new ConcreteObserver();
Subject subject = new ConcreteSubject();
subject.addObserver(o1);
subject.addObserver(o2);
subject.notifyAllObservers();
}
}
junit3.8源码的实现:
首先定义Observer观察者的就是TestListener,它是一个接口,定义了几个方法,说明它监听的几个方法。如测试开始,发生失败,发生错误,测试结束等监听事件的时间点。由具体的类来实现。
// A Listener for test progress
public interface TestListener {
// An error occurred.
public void addError(Test test, Throwable t);
// A failure occurred.
public void addFailure(Test test, AssertionFailedError t);
// A test started.
public void startTest(Test test);
//A test ended.
public void endTest(Test test);
}
在JUnit里有三种方式来实现TestListener,如TextUI,AWTUi,SwingUI并且很容易使开发人员进行扩展,只需实现TestListener即可。下面看在TextUi方式是如何实现的,它由一个类ResultPrinter实现
public class ResultPrinter implements TestListener {
PrintStream fWriter; * A test ended.
public PrintStream getWriter() {
return fWriter;
}
public void startTest(Test test) {
getWriter().print(".");
}
public void addError(Test test, Throwable t) {
getWriter().print("E");
}
public void addFailure(Test test, AssertionFailedError t) {
getWriter().print("F");
}
public void endTest(Test test) {
}
}
在JUnit中使用TestResult来收集测试的结果,它使用Collecting Parameter(收集参数)设计模式(The Smalltalk Best Practice Patterns中有介绍),它实际是ConcreteSubject,在JUnit中Subject和ConcreteSubject是同一个类,我们看它的实现
public class TestResult extends Object {
//使用Vector来保存,事件的监听者
protected Vector fListeners = new Vector();
// Registers a TestListener
public synchronized void addListener(TestListener listener) {
fListeners.addElement(listener);
}
//Unregisters a TestListener
public synchronized void removeListener(TestListener listener) {
fListeners.removeElement(listener);
}
//Informs the result that a test will be started.
public void startTest(Test test) {
for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
((TestListener)e.nextElement()).startTest(test);
}
}
//Adds an error to the list of errors. The passed in exception
//caused the error.
public synchronized void addError(Test test, Throwable t) {
for (Enumeration e= cloneListeners().elements(); e.hasMoreElements(); ) {
((TestListener)e.nextElement()).addError(test, t);
}
}
//以下省略了addFailure和endTest代码
}
效果
我们来考虑经过使用Observer模式后给系统的架构带来了那些效果:
1、Subject和Observer之间地抽象耦合一个TestResult所知道的仅仅是它有一系列的观察者,每个观察者都实现TestListener接口,TestResult不必知道任何观察者属于哪一个具体的实现类,这样使TestResult和观察者之间的耦合是抽象的和最小的。
2、支持广播通信被观察者TestResult会向所有的登记过的观察者如ResultPrinter发出通知。这样不像通常的请求,通知的发送不需指定它的接收者,目标对象并不关心到底有多少对象对自己感兴趣,它唯一的职责就是通知它的观察者。
分享到:
相关推荐
《Junit设计模式分析》这本书深入探讨了如何在单元测试框架Junit中巧妙地应用设计模式,以提高代码的可测试性和可维护性。在软件开发过程中,设计模式是解决常见问题的最佳实践,它们能够帮助开发者创建灵活、可扩展...
总之,JUnit的设计模式分析展示了如何将经典设计模式融入实际项目中,以达到简化代码、增强可维护性和提高代码复用性的目的。理解这些模式及其在JUnit中的应用,对于提升Java测试能力及软件开发实践具有重要意义。
本资源"Junit设计模式分析(带源码)"旨在深入探讨JUnit在设计上的模式和最佳实践,通过源码分析帮助开发者更好地理解和应用这个工具。 1. 单元测试基础: 单元测试是对软件中的最小可测试单元进行检查,如函数、...
《JUnit设计模式分析》 JUnit,作为Java编程领域中最广泛使用的单元测试框架,它的重要性不言而喻。在软件开发过程中,单元测试是确保代码质量、可维护性和可扩展性的重要手段。本分析将深入探讨JUnit的设计模式,...
以下是一些在JUnit中常见的设计模式: 1. 工厂模式:JUnit通过TestSuite类实现了工厂模式,它可以根据测试类动态创建测试集合。这使得用户可以方便地组合不同的测试类进行批量执行。 2. 单例模式:JUnit的Test...
观察者模式在JUnit中也得到了广泛应用,比如`TestWatcher`,它监听测试的生命周期事件,如测试开始、结束、失败或成功。开发者可以通过扩展这个类来定制测试事件的通知行为。 此外,JUnit还利用了装饰者模式,例如...
5. **观察者模式**:JUnit实现了Observer设计模式,通过TestListener接口通知测试事件,如测试开始、结束、失败等,使得外部系统可以监听测试过程并做出相应反应。 6. **模板方法模式**:JUnit的TestWatcher是一个...
《Junit设计模式应用》是基于作者业余时间的翻译成果,旨在通过设计模式的角度深入剖析JUnit的内在原理,以此促进读者对单元测试框架理解和运用能力的提升。设计模式是软件工程中的宝贵经验总结,它为解决常见问题...
JUnit的架构设计遵循了简单而高效的原则,同时采用了多种设计模式,如工厂模式用于创建测试实例,单例模式用于全局唯一的测试运行器,观察者模式用于事件通知等。这些模式的运用使得JUnit具有高度的可扩展性和可维护...
### JUnit源码及其涉及的设计模式 #### 一、引言 JUnit作为一款广泛应用于Java项目的单元测试框架,其设计理念和实现方式对于软件开发者来说具有很高的学习价值。本文将深入探讨JUnit源码,并重点关注其中使用的...
当一个对象的改变同时会影响其他对象的行为的时候,可以使用此设计模式。 l 主题对象 :一个需要被关注的主题对象,这个主题对象改变会影响其他对象的行为 l 订阅对象:当主题对象发生改变,订阅对象需要相应处理...
3. 观察者模式:验证观察者是否能正确订阅和接收主题发布的事件,同时检查解订阅功能是否正常。 4. 建造者模式:测试建造者是否能按预期构建复杂对象,确保各个步骤的正确性。 5. 装饰器模式:确保装饰者能在不...
9. **源码分析**:可能深入到JUnit的源代码,解释其设计模式,如观察者模式、装饰器模式,以及JUnit如何处理测试失败和测试报告。 10. **实战示例**:结合实际项目,展示如何利用JUnit进行单元测试,解决实际问题。...
3. **设计模式** - 表明项目的核心在于使用了软件工程中的设计模式,这些模式是解决常见问题的最佳实践,如单例模式、工厂模式、观察者模式等,它们有助于提高代码质量和可读性。 【文件名称列表】:由于没有提供...
- 观察者模式是一种基于发布/订阅模式的行为设计模式,适用于构建响应式的系统。 - 当一个主题的状态发生变化时,所有观察该主题的对象都会收到通知,并能够自动更新自己的状态。 - `java.util.Observable`类和`java...
在设计模式方面,JUnit 4.8.2体现了观察者模式,测试运行器(Runner)观察并执行测试,当测试完成时,它可以通知其他系统组件(如构建工具或IDE)测试的结果。此外,扩展机制如自定义测试运行器(Custom Test ...