| <!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> |