Chapter 17. Decouple with Interfaces

Wizard of Oz: “Pay no attention to that man behind the curtain.”

The Wizard of Oz

Developers can create acceptance tests for service implementations. The tests can be both for accuracy and for performance.


Tests for a Service Provider

Cathy wants the ZIP code to be checked for customer contact addresses. Debbie needs a service for the verification of a ZIP code for an address; she is not going to code the service herself, but obtain it from another group. Dave is another developer who is going to provide that service. As a backup plan in case Dave cannot implement the service, Debbie may purchase it from an external vendor.

As the requester, Debbie needs to create acceptance tests for the service that Dave will use to test his implementation. The tests should be independent of the service provider. Ideally, she should not have to rewrite any of her production code if the service provider is changed, so she creates an application programming interface (API) that Dave should implement [Pugh01]. If she uses another service provider, she will adapt its interface to this API. The tests that she writes as well as her production application are coded to the API.

The Interface

Debbie creates an API that describes what she wants the ZIP code verification interface to look like:

image

“ZipCode” is a data class that contains the ZIP code. The lookupZipCode() method needs to indicate an error if the ZIP code cannot be found. From a programmer’s point of view, if the ZIP code was not findable, the method could return the value of null or throw an exception that contained further information. The “StateID” is a data class that contains a reference to a valid U.S. state or territory.

There are other things that Debbie may need, such as the ability to get the city and state that correspond to a ZIP code. If so, she would add that to the interface.

Debbie provides the following table as an example of the tests she is going to run against the interface.

image

Dave may create an implementation that uses a subscription to the United States Postal Service (USPS) web-service, a corporately-owned address validation program, or some other system. If Debbie needs a quick way to do an end-to-end test, she might create a browser-simulator version that interacts with the usps.com website and provides the answers found on that site. If she needs a way to have a quick unit test for her code that uses this service, she might create a “test double” (see Chapter 11 “System Boundary”) for ZipCodeLookup. The test double might only return a small set of ZIP codes, such as the set listed in this table.

The “StateID” data class listed in the interface specification needs to be defined. Debbie can use a table to do that. She could include just the states or all USPS recognized abbreviations.

image

The ZIP Code Lookup tests do not have to include states that are not in this table.

Quality Attribute Tests

Debbie can provide quality attribute tests for an interface implementation. The initial test checks that the interface is working properly. She may also be concerned with the performance of the implementation. If it takes a long time to look up a ZIP code, then the user experience will suffer. So she can specify the amount of time in a test. The time limits may differ based on the type of result (success or failure) and the particulars of the data being passed to the service.

image

For a data-lookup style service, such as the ZIP code, Debbie could also make up acceptance criteria for the completeness and accuracy of the data. The criteria can be specified in a table such as:

image

Determining whether an implementation meets these criteria is a difficult task and beyond the scope of this book. However if you have two implementations of a service, you can at least cross-check them.

Comparing Implementations

In the case of ZIP Code Lookup, Debbie may be able to obtain two implementations of the interface. She may not know in advance what the correct results are. All she knows is that the results of the two implementations need to match. This is often the case when you have an existing system and you are rewriting the system to use another technology. The external behavior of the new system must match the existing system. So you first run one implementation and store the results. This forms the acceptable values (the oracle [Bach01]) for the new system. Then you run the second implementation and compare the results to those found in the first implementation.

There are at least two ways to do this. The first way involves using variables. It’s introduced here to show how a test can reuse results from earlier in a test. The second way eliminates some redundancy.

Using tables to represent this comparison involves creating some type of variable. A variable is used to store the results of one action for use in a later action. You need to be able have a way to show when a value is stored in the variable and when it is retrieved. For the purposes of demonstration, we’ll use a → symbol to show a value is stored into a variable and a → symbol to show the value is retrieved from the variable.1 Debbie creates the following table for the first implementation of the interface.

image

The ZIP codes are stored in variables named eastoakzip, pennylanezip, and noplacezip. Debbie then makes the next table for the second implementation.

image

The ZIP codes returned by this implementation are compared to values stored in eastoakzip, pennylanezip, and noplacezip. If a value does not match, an error is indicated. If an error appears, Debbie cannot be sure which implementation was wrong without further investigation.

If the number of comparisons was large, Debbie might use a second way to compare the ZIP codes that eliminates some redundancy. Rather than use a table to list the individual data items, she might create a module that does the comparison internally. So all she would list are the input values. If a mismatch occurs between the two implementations, the two answers can be shown in the ZIP Code column as follows:

image


Separating User Interface from Service

Debbie has not specified how the state in the address is going to appear in the user interface. She knows that separating the business rules from the display makes for easier testing, as shown in Chapter 14, “Separate View from Model.” There are at least four ways the state could appear on the user interface:

• A text box that accepts two-character abbreviations for the state

• A text box that accepts the full name for the state

• A drop-down list that contains the two-character abbreviations for each state

• A drop-down list that contains the full name for each state2

These are four display manifestations of the same requirement; they are not four different requirements. They differ in the user experience. In the drop-down version, users cannot enter an incorrect state, but they may have to type more.3 The selection of one is based on user quality feedback.

In any event, the field for the ZIP code should only allow either five or nine digits to be entered. These are the two valid lengths for U.S. ZIP codes. Allowing other characters or lengths would cause unnecessary calls to the ZIP Code Lookup.

If Debbie had a field that allowed the user to enter a ZIP code, she would check that ZIP code against the one returned by ZIP Code Lookup. How she displays a mismatch is based on the desired user experience. The mismatch might show up in a dialog box, as a line at the top of the dialog box, as a message next to the ZIP code, or as a different colored ZIP code.

Separation of Concerns

The preceding example is being pretty U.S centric. Debbie might need to do postal code matching for all the countries in the world. To keep things simple, she could have a master table of all the countries that breaks out to tables of tests for each country.

image


Reusable Business Rules

A business rule is something that is true regardless of the technology employed (paper, computer, and so on). The rule that a ZIP code be valid for an address is true regardless of whether the envelope is printed on a laser printer or handwritten. Implementations of business rules should be exposed so that they can be used in multiple places, not just the middle tier.

For example, the user interface may require business rule checking to allow errors to be identified in a more user-friendly manner. The ZIP code for customer address may be verified as part of the input process. To avoid duplication of functionality that would require duplication of testing, the user interface should use the same module as the middle tier. The means for doing so depend on the technology involved and are beyond the scope of this book.4

Reuse extends beyond a single application. If a function, such as ZIP Code Lookup, is used by multiple applications, Debbie would put the component into an infrastructure or core system library. That would eliminate needing to have tests applied to each application.


Summary

• Developer acceptance tests should be created for every service.

• A common API should be created for services that have multiple implementations.

• Create performance tests for service implementations.

• Create completeness and accuracy criteria for service implementation, when appropriate.

• Create comparative tests for checking old versus new implementations.

• Keep tests for services separate from tests for user interfaces.

• Consider whether services are application services or core services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.236