Pruebas a nivel de subsistema es un nivel de pruebas muy importante, centrado en verificar que los componentes respetan los contratos de interfaz y se integran limpiamente con otros componentes. Las pruebas de subsistemas cubren el vacío entre las pruebas a nivel de clase y las pruebas a nivel de sistema y resulta especialmente útil para gestionar el ámbito y el foco de atención.
Si toda la suite de pruebas se centrara en las pruebas a nivel de clase, acabaría teniendo problemas de integración. Por otra parte, centrarse solamente en las pruebas a nivel de subsistema proporcionaría una cobertura superficial de las pruebas ya que no le ofrece suficiente control sobre las clases individuales.
Utilice el siguiente proceso general para probar un subsistema:
En teoría, debería emplear la mayor parte del proceso de pruebas realizando pruebas a nivel de clase e identificando grupos de clases que puede integrar y probar como un subsistema. Para definir el contenido de un subsistema, es necesario identificar esas clases que proporcionan un servicio cohesivo. Deberá basar esta selección en los siguientes criterios:
Tras identificar un grupo de clases que desea probar como un subsistema, utilice el análisis basado en estado como ayuda para comprender a alto nivel los casos prácticos que desea verificar. Dado que está tratando con un subsistema, deberá tener en cuenta un comportamiento de agregado. Se denomina comportamiento de agregado cuando se llega a un determinado estado, implicando que muchos objetos individuales han llegado a ese estado específico.
Por ejemplo, imagine un sistema de bienes inmuebles que gestiona el proceso de comprar una casa. Este tipo de sistema podría tener varios estados, como por ejemplo PreapprovedCustomer, OfferAccepted, HomeInspectionCompleted y PurchaseAndSaleSigned.
También podría haber varias clases que definan el sistema, tales como Customer, House, Seller, Visit, Offer, Lender, Appraiser, Mortgage, CreditReport, CreditBureau, ApprovalLetter, HomeInspection, PurchaseandSale, LoanApplication, ClosingContract, Commission, etcétera.
En este ejemplo, puede considerar un estado concreto, por ejemplo el estado PurchaseAndSaleSigned, como el comportamiento de agregado de varias clases y estados, como se indica a continuación:
Clase | Estado |
---|---|
Customer | UnderAgreement |
House | UnderAgreement |
Seller | UnderAgreement |
Offer | Accepted |
ApprovalLetter | Delivered |
HomeInspection | Completed |
CreditReport | UptoDate |
Para crear un caso práctico de prueba, tenga en cuenta las distintas acciones que deben producirse para poner el sistema en estado PurchaseAndSaleSigned. El caso práctico de prueba deberá realizar acciones individuales para que en este ejemplo de bienes inmobiliarios suceda lo siguiente:
En este punto crearía una prueba. La manera más fácil de hacerlo es generar una prueba para un método en la primera clase con la que interactuaría. En este caso, podría seleccionar el método getCreditReport de la clase Customer ya que es el primer método que se invocaría en el caso práctico. A medida que construye el caso práctico, añada los objetos que la prueba necesite. Asegúrese de cubrir todos los estados y transiciones del subsistema. Tras finalizar, defina valores a utilizar en todo el caso práctico. Debido a la amplia gama de casos prácticos potenciales, es mejor mantener los datos de prueba no complicados, utilizando valores típicos y un número limitado de particiones de datos.
Tras definir un flujo típico a través del diagrama de estado, defina casos prácticos adicionales para aumentar al máximo la cobertura de las pruebas. Estos casos prácticos deben ejercer los diversos estados y transiciones del subsistema. A menudo, un caso práctico es similar a otro definido anteriormente. En esta situación, creará nuevas pruebas basadas en pruebas definidas anteriormente. En las nuevas pruebas, invocará el controlador de pruebas definido anteriormente y añadirá nuevos mensajes al nuevo diagrama de secuencia de pruebas para completar el flujo.
De esta manera puede construir casos prácticos complejos reutilizando flujos definidos previamente y crear solamente las pequeñas variaciones necesarias para cubrir las nuevas transiciones y estados.