Plugin Test Practices
插件测试实践
In our own testing we have discovered a number of common test patterns. In order to encourage a consistent approach for testing they are published below.
在我们的测试中,我们发现有几个共同的测试模式。为了鼓励测试方式的一致性,我们发布了以下规则。
Setters, Getters and Commands
属性的设置子、获取子及命令
Scenario: You want to test setters, getters, and commands in an interface.
情景:你打算测试一个接口中属性的设置子、获取子及命令。
Method: Call the setter or command which affects the object state. Call the getter to verify that state. If you can't see this state see to "Piercing an Interface".
方法:如某些设置子或命令会影响了对象的状态,当调用他们时,应调用属性获取子来验证状态。如果你不能访问状态时,参考“穿透一个接口”
Subclass Superclass
子类 超类
Scenario: You want to test D, which is a subclass of B.
情景:你想测试D, 他是B的子类。
Method: Implement a test case for B called BTest. Then create a subclass of BTest called DTest which tests D the additional methods on D. Add a factory method to BTest to create any required test objects which may be specific to the test case and override it in DTest. When this approach is taken you inherit all of the BTest methods within the subclass DTest.
方法:为B类实现一个TestCase叫BTest。然后建立一个BTest的子类DTest,它是为测试D类的,DTest拥有一些针对D的方法。在BTest中添加一个工厂方法来创建任何需要被测试的对象,这些对象可能对TestCase有特殊的含义。在Dtest Override这个方法。当在子类和超类中十分相似时,你可以在子类DTest中继承所有BTest的方法。
Event Notification
事件通知
Scenario: You want to test Source, a class which fires events when a particular situation occurs.
情景: 你准备测试代码,在代码中有一个类,当有一个特殊的场合发生时内部会触发若干事件。
Method: Implement a listener for Source which can record the reception of events. Then write a test class for Source called SourceTest which does something which should cause those events to fire. Verify the reception of events afterwards.
方法:实现一个监听器,它可以记录接收到事件。然后、写TestCase类XXXTest(XXX代码名),XXXTest可以做一些触发这些事件的动作。然后、检验接收到的事件。
Additional Tips: The CallHistory class in org.eclipse.ui.tests.util can be used to record the methods invoked in a target object. If the listener can be added then test the removal of the listener also. Make sure that events are not received after the listener has been removed.
提示:在org.eclipse.ui.tests.util中类CallHistory可以用来记录在目标对象中方法的调用。如果监听器可以被添加,然后再移除。请在监听器移除后确保事件没有接收。
Avoiding Global State
避免全局的状态
Scenario: In Eclipse the workbench is a global object. Unfortunately, this means that one test case may modify that state of the workbench and affect the outcome of other unrelated test cases. How can you avoid this problem?
情景:在Eclipse工作台是一个全局对象。不幸地是,它意味着一个TestCase有可能改变工作台地状态,并且影响其它不相干地TestCase。你将如何避免这个问题?
Method: If the test case modifies the state of a window or something in the window you should create a new window as part of the setUp for the test case. Run the test code within that window and then close the test window in the tearDown method. The modified state will be discarded when the window is closed.
方法:如果TestCase改变了一个窗体或者窗体中的东东,你应该在TestCase的setUp中创建一个新的窗体。在新创建的窗体中运行测试代码,并且在tearDown方法中关闭这个测试窗口。被改变的状态将随着窗体的关闭而丢弃。
Additional Tips: The UITestCase class in org.eclipse.ui.tests.util can be used as a superclass for your test case. It provides useful methods for the creation of new windows, pages, etc, and their disposal.
提示:org.eclipse.ui.tests.util中UITestCase可以用来做TestCase的父类。它提供一些有用的方法,l例如:创建新的窗体、页面等,以及处理它们。
(备注:在Eclipse中一个Workbench可以有多个WorkbenchWindow,而视图、菜单等均附着于WorkbenchWindow之上)
Piercing the Encapsulation
穿透封装
Scenario: To test the behavior of commands which modify the state of the object when there are no public interfaces to query that state.
情景:想测试会改变对象状态的命令行为,但是这个对象没有Public接口来访问它的状态。
Method: If possible, cast the interface to a concrete class with additional public methods. For instance, in the workbench the underlying structure for IWorkbench is exposed in Workbench. Given a Workbench object, you can get the menu, toolbar, etc, and interact directly with those objects to verify their state or invoke them directly.
方法:如果可能的话,在实现接口的类中添加Public方法。例如,在工作台中,IWorkbench中其内部的结构暴露在类Workbench中。获得一个Workbench对象,你可以获得menu,toolbar等,并且直接使用(可能是直接强制转换的意思)这些对象来校验它们的状态或者直接调用他们。
Additional Tips: The ActionUtil class in org.eclipse.ui.tests.util can be used to invoke actions within a window or menu manager.
提示:org.eclipse.ui.tests.util中ActionUtil类可以用来在窗体或menu manager内调用Action(类似Delphi中的Action,它是菜单条和工具栏按钮等的界面上的统一抽象)。
(备注:在Eclipse中对外公开都是Interface,而具体的类都在internal包中,使用者不应使用internal内部的对象。当然Java可以在Interface里定义常数和常量,呵呵~~)
Extension Creation
扩展创建
Scenario: You want to test the creation of an extension. For instance, the IWorkbenchPage has a method called openEditor which creates an editor extension, and another called showView which creates a view extension. How do you test these?
情景:你想去测试一个扩展的创建。例如,一个IWorkbenchPage 有一个openEditor方法,在openEditor中创建了一个editor extendsion(编辑器扩展),并且有另一个创建视图扩展的showView方法。你如何测试他们。
Method: Obviously we can test these methods by invoking them. However, each of them take an editor or view id. Which id's do we use? If we reference views and editors which exist within the Workbench UI Standard Components project the test case is vulnerable to change in those components. But we're not testing those standard components, we're actually testing IWorkbenchPage, so it is better to implement some light weight mock views and editors which do nothing more than record their own creation and lifecycle.
方法:很明显我们可以通过执行它们来测试。当时,它们都需要一个编辑器或视图的ID。我们将使用什么id呢?如果我们关联的视图是已经在工作台中存在,在标准部件项目中对于此TestCase在改变这些部件中将十分脆弱。因此、实现一些轻磅的Mock视图和Mock编辑器,将会更好。在这些Mock Object中处理记录它们创建和生命周期其它什么也不作。
Extension Lifecycle
扩展生命周期
Scenario: Within the workbench there are various interfaces, such as IViewPart, which are defined as API and implemented by plugin code. There is no need to test the implementation of an interface like this if you define it and expect others to implement it. However, it is important to test the lifecycle of the object as implemented by those objects which create and call the interface implementation.
情景:在Workbench中有几个不同的接口,例如,IViewPart,这些接口做为提供给使用者的API并且在PlugIn代码中实现。如同以上,如果你仅仅定义了一些接口并且期望其它PlugIn来实现它,那么没有必要测试这些接口的实现。但是,关于创建及使用这些接口的对象,针对它们的对象生命周期的测试,是十分重要的。
Method: Define a class X which implements the interface and records the invocation of various methods. Create a scenario where this class is instantiated and should receive events. Afterwards, test that those methods were called.
Additional Tips: The CallHistory class in org.eclipse.ui.tests.util can be used to record the methods invoked in a target object.
方法:定义一个类X,来实现这个对象并且记录不同方法的调用。创建一个这样的情景,这个类被实例化并应该接收事件。然后,测试这些方法的调用。
提示:可以利用org.eclipse.ui.tests.util中CallHistory。
Session Persistence
Session的持续化
Scenario: You want to test the persistence of state from one session to the next.
情景:你希望持续化一个会晤进入下一个时的状态。
Method: You need to create two test cases. One test case will set up the state. The other will verify the state. Run them sequentially in two separate processes.
方法:你需要创建两个TestCase。一个TestCase将建立状态。另一个将校验状态。在两个分离的进程连续的运行它们。
Testing Mixins
测试最小化
Scenario: Within the workbench there are
many classes which implement a particular interface. Given the
responsability defined by the class and the interfaces separately, how do you
organize the test cases?
情景:在Workbench中,有很多实现特定接口的类。这些类和接口都分别给出其响应的定义,你如何组织这些TestCase呢?
Method: You may choose to use a one to one rule of thumb. Create one test case class for each class or interface in the mixin. This hierarchical separation makes it easier to reuse each test case when an interface is implemented by more than one class. It also simplifies the accounting required to track which test cases have been written.
方法:你可以选择使用一系列经验法则。为每一个类和接口最小化地创建TestCase类。它们按层次来分离,这样当一个接口被多个类实现时,将更容易重用这些TestCase。它也应该容易被跟踪、计数那些TestCase已经写了。
Where Do I Stop?
我什么地方停止?
Scenario: The workbench has many layers. If you are a plugin developer what are the bounds of the area you should test?
情景:Workbench有很多层。如果你是一个PlugIn开发者,你测试的范围是那些呢?
Method: In general, you should assume that everything you rely upon works. If you don't take this approach you will spend endless hours testing code written by other people. For instance, in the workbench UI we don't test JFace, SWT, or core. They have their own test suites and are assumed to work. On the other hand, you should at least write tests for every API which you provide to others. In Eclipse we have very strong rules for API compatability, and your own test cases allow you to change the implementation of API with some confidence.
方法:通常你应该假定对所有的代码可以放心地工作。如果你去不努力接近此目标,你将无止境地把时间消耗在调试别人写的代码中。举个例子,在Workbench UI中我们没有测试Jface, SWT,或者core。这些模块有其自身的Test Suite并且假定可以工作。在另一方面,关于提供给别人的API,你至少应该为每个API都写测试。在Eclipse中对于API的兼容性,我们有非常强硬的条律,并且你的TestCase也使得你有信心去改变这些API的实现。