<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
<meta name="Author" content="Build"> | |
<meta name="GENERATOR" content="Microsoft FrontPage 4.0"> | |
<title>The Workbench Test Framework</title> | |
</head> | |
<body> | |
<h3> | |
Plugin Test Practices</h3> | |
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. | |
<p><b>Setters, Getters and Commands</b> | |
<p>Scenario: You want to test setters, getters, and commands in an interface. | |
<p>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". | |
<p><b>Subclass Superclass</b> | |
<p>Scenario: You want to test D, which is a subclass of B. | |
<p>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. | |
<p><b>Event Notification</b> | |
<p>Scenario: You want to test Source, a class which fires events when a | |
particular situation occurs. | |
<p>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. | |
<p>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. | |
<p><b>Avoiding Global State</b> | |
<p>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? | |
<p>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. | |
<p>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. | |
<p><b>Piercing the Encapsulation</b> | |
<p>Scenario: To test the behavior of commands which modify the state of | |
the object when there are no public interfaces to query that state. | |
<p>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. | |
<p>Additional Tips: The ActionUtil class in org.eclipse.ui.tests.util can | |
be used to invoke actions within a window or menu manager. | |
<p><b>Extension Creation</b> | |
<p>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? | |
<p>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. | |
<p><b>Extension Lifecycle</b> | |
<p>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. | |
<p>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. | |
<p>Additional Tips: The CallHistory class in org.eclipse.ui.tests.util can | |
be used to record the methods invoked in a target object. | |
<p><b>Session Persistence</b> | |
<p>Scenario: You want to test the persistence of state from one session | |
to the next. | |
<p>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. | |
<p><b>Testing Mixins</b> | |
<p>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? | |
<p>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. | |
<p><b>Where Do I Stop?</b> | |
<p>Scenario: The workbench has many layers. If you are a plugin developer | |
what are the bounds of the area you should test? | |
<p>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. | |
<br> | |
<br> | |
</body> | |
</html> |