32.4. The AdapterInitially, Emily had encoded the GUI operations directly in the fixture, but she had realized that it would be better to have an adapter. The adapter code is shown in Listing 32.2. This code uses a GuiAdapter[3] to operate on the components, such as JButtons, of the running GUI.
The adapter provides two API methods, as called from the fixture. The first method, addRentalItem(), emulates the operations of the user through the GUI, as shown in Figure 32.2, to add the details of a new rental item.
This GuiAdapter depends on the Swing components of interest being explicitly named. For example, the first JTextField is named "nameField", as shown in part of the code for the dialog box in Listing 32.3. The second method of class RentEzeUiAdapter, getrentalItems(), is shown in Listing 32.2. This method passes the actual collection from the JTable, named "table", to a SetFixture. This method makes use of the general-purpose method collectionOfJTable() in class GuiAdapter, which takes the internal name of a JTable and returns a list of Map objects, one for each row in the table. A Map defines, for a row, the mapping between each JTable column name and the corresponding value from the table. Listing 32.2. RentEzeUiAdapter.javapublic class RentEzeUiAdapter { GuiAdapter frameAdapter; public RentEzeUiAdapter(JFrame frame) { frameAdapter = new GuiAdapter(frame); } public void addRentalItem(String name, int count, double hourlyRate) throws Throwable { frameAdapter.pressTab("tabbedPane",1); // Press tab for Rental frameAdapter.pressButton("addButton"); GuiAdapter dialogAdapter = frameAdapter.getAdapterForFirstDialog(); dialogAdapter.enterString("nameField",name); dialogAdapter.enterString("count",""+count); dialogAdapter.enterString("hourlyRateField",""+hourlyRate); dialogAdapter.pressButton("addButton"); } public List getRentalItems() throws Throwable { frameAdapter.pressTab("tabbedPane",1); // Press tab for Rental return frameAdapter.collectionOfJTable("table"); } } Listing 32.3. EditRentalItemDialog.javapublic class EditRentalItemDialog extends JDialog { private JTextField nameField = new JTextField(20); private JTextField countField = new JTextField(); private JTextField hourlyRateField = new JTextField(); public EditRentalItemDialog(JFrame frame, RentalItem item) { super(frame, "Edit Rental Item", false); // ... nameField.setName("nameField"); hourlyRateField.setName("hourlyRateField"); countField.setName("count"); } // ... } Figure 32.4 shows the interaction during setup of the first two tables of Figure 32.1 and the fixture, GUI, and application objects. Figure 32.4. Testing Through the GUI
Note The adapter acts as mapping layer that maps each API action into a sequence of operations on the GUI. Having a separate layer means that we can write the fixtures for the intended API, as it develops. Once this mapping layer is in place for a part of the API, the corresponding part of the system can be untangled. Once we have a means of switching the tests so that they can be run through the GUI or the API or both, we can be sure that mistakes aren't introduced and left. Note SetFixture, ArrayFixture, and SubsetFixture all interpret Map objects in an actual collection in a special way, as introduced in Section 28.9 on p. 243. Instead of checking for an instance variable or a property that corresponds to the Fit table header name, the fixture looks for a mapping from that name to a value. For consistency with the treatment of table headers, extended camel casing[a] is applied to a JTable column name before it is used as a key into the Map.
|