Subsystem-level testing is a very important test level aimed at verifying that components respect their interface contracts and integrate cleanly with other components. Subsystem testing bridges the gap between class-level testing and system-level testing and is especially helpful in managing scope and focus.
If your entire test suite were focused on class-level testing, you would end up with integration problems. On the other hand, focusing only on subsystem-level testing would provide superficial test coverage because it does not give you enough control over the individual classes.
Use the following general process to test a subsystem:
In theory, you should spend most of your testing effort doing class-level testing and identifying groups of classes that you can integrate together and test as a subsystem. To define the content of a subsystem, you need to identify those classes that provide a cohesive service. You should base this selection on the following criteria:
After you identify a group of classes that you want to test as a subsystem, use state-based analysis to help you understand at a high level the scenarios that you want to verify. Since you are dealing with a subsystem, you will have to consider an aggregate behavior. We speak about aggregate behavior because when a certain state is reached, it implies that many individual objects have reached that specific state.
For example, consider a real estate system that manages the process of buying a house. This type of system could conceivably have several states, such as PreapprovedCustomer, OfferAccepted, HomeInspectionCompleted, and PurchaseAndSaleSigned.
There could also be several classes that define the subsystem, such as Customer, House, Seller, Visit, Offer, Lender, Appraiser, Mortgage, CreditReport, CreditBureau, ApprovalLetter, HomeInspection, PurchaseandSale, LoanApplication, ClosingContract, Commission, and so on.
In this example, you can think of a certain state, for example the PurchaseAndSaleSigned state, as the aggregate behavior of several classes and states, as follows:
Class | State |
---|---|
Customer | UnderAgreement |
House | UnderAgreement |
Seller | UnderAgreement |
Offer | Accepted |
ApprovalLetter | Delivered |
HomeInspection | Completed |
CreditReport | UptoDate |
To create a test scenario, consider the different actions that need to occur to put the system in the PurchaseAndSaleSigned state. The test scenario should perform individual actions so that in our real estate example:
At this point, you would create a test. The easiest way to do this is to generate a test for a method in the first class that you would interact with. In this case, you could select the getCreditReport method from the Customer class because it is the first method that would be invoked in the scenario. As you build the scenario, add the objects that your test needs. Be sure to cover all of the states and transitions of the subsystem. After you finish, define values to be used throughout the scenario. Because of the wide range of potential test scenarios, it is best to keep the test data fairly simple, using typical values and a limited number of data partitions.
After defining a typical flow through the state chart diagram, define additional scenarios to maximize your test coverage. These scenarios need to exercise the various states and transitions of the subsystem. Often, one scenario is similar to another that was defined previously. In this situation, you would create new tests based on previously-defined tests. In the new tests, you would invoke the previously-defined test driver and add new messages to the new test sequence diagram to complete the flow.
In this way, you can build up complex scenarios reusing previously defined flows and create only the small variations necessary to cover new transitions and states.
Related concepts
Test strategies
State-based testing techniques
Related tasks
Testing Java methods
Testing Java classes