Seminario web destacado: Pruebas de API mejoradas con IA: un enfoque de prueba sin código | Vea ahora
Un comienzo esencial en el camino hacia la entrega de alta calidad en tiempo real. software embebido Systems está incorporando el análisis estático en el flujo de trabajo de desarrollo. Con el análisis estático, los ingenieros de software pueden mejorar la confiabilidad, el rendimiento y la seguridad de su software. Al mismo tiempo, pueden reducir el costo y el tiempo asociados con la identificación y reparación de defectos más adelante en el ciclo de desarrollo.
El análisis estático identifica errores en el código en una etapa temprana del proceso de desarrollo, mucho antes de que el software se ejecute en el hardware de destino. Esto incluye la detección de errores de sintaxis, errores lógicos y posibles errores de tiempo de ejecución. La seguridad también es primordial en los sistemas integrados, especialmente aquellos que forman parte de una infraestructura crítica o dispositivos de Internet de las cosas (IoT). El análisis estático puede descubrir vulnerabilidades como desbordamientos de búfer, fallas de validación de entradas y otras debilidades de seguridad que los atacantes podrían aprovechar.
Además, la mayoría de los equipos de desarrollo estarán de acuerdo en que las pruebas unitarias también son esenciales para el desarrollo de software integrado a pesar del esfuerzo y los costos. Las pruebas unitarias ayudan a los desarrolladores a comprender realmente el código que están desarrollando y proporcionan una base sólida para un régimen de verificación y validación necesario para satisfacer los objetivos de seguridad de un producto. Construir sobre esta base de pruebas unitarias permite a los equipos acelerar el desarrollo ágil y al mismo tiempo mitigar el riesgo de que los defectos pasen a etapas posteriores del proceso.
¿Por qué realizar pruebas automatizadas?
Análisis estático
La automatización del análisis estático en el flujo de trabajo de desarrollo de software proporciona importantes beneficios.
- Se integra en IDE de desarrollador y canalizaciones de CI/CD. Integrar el análisis estático en el proceso de construcción de toda la organización de desarrollo es una buena práctica. Con la automatización, los equipos pueden integrar el análisis estático en los IDE de los desarrolladores para realizar un análisis rápido mientras escriben el código. Cuando los equipos integran el análisis estático en los procesos de CI/CD, el código se analiza automáticamente cada vez que envían cambios de código.
Esto garantiza que los problemas se detecten y solucionen de inmediato, manteniendo la calidad del código durante todo el proceso de desarrollo, lo cual es esencial para equipos grandes o proyectos con ciclos de desarrollo rápidos. Hará cumplir los estándares de codificación en toda la base del código, eliminando el error humano y el sesgo en las revisiones del código. Este enfoque proactivo también garantizará la seguridad al identificar posibles vulnerabilidades que deben mitigarse antes de que puedan explotarse.
- Garantiza el cumplimiento continuo. El análisis estático automatizado garantiza el cumplimiento continuo de estándares relevantes como MISRA y CERT para industrias para pruebas de software integrado como automoción, dispositivos médicos y aeroespacial con estrictos requisitos reglamentarios. Esto es crucial para aprobar auditorías y certificaciones que requieren análisis estático.
- Elimina tareas repetitivas y mundanas. Los desarrolladores no tienen que realizar tareas repetitivas y mundanas de revisar el código y asignar violaciones de codificación identificadas para corregirlas. Parasoft aplica soluciones patentadas de inteligencia artificial y aprendizaje automático al flujo de trabajo de análisis estático para priorizar los hallazgos de violaciones de reglas y agilizar los pasos de remediación, lo que reduce inmediatamente el esfuerzo del equipo para adoptar el análisis estático. Los desarrolladores pueden centrarse en aspectos más complejos y creativos del desarrollo de software, lo que conduce a una mayor satisfacción laboral y productividad.
Examen de la unidad
Si bien el análisis estático proporciona información valiosa sobre la calidad del código y los posibles problemas sin ejecución, las pruebas unitarias verifican el comportamiento real y la corrección del código en ejecución. El uso conjunto de ambas técnicas garantiza un proceso de desarrollo de software más sólido y confiable, ya que se complementan entre sí y cubren una gama más amplia de problemas potenciales.
Sin embargo, a pesar de los beneficios, los equipos de desarrollo a menudo tienen dificultades para realizar suficientes pruebas unitarias. Las limitaciones en la cantidad de pruebas se deben a múltiples factores, como la presión para ofrecer rápidamente una mayor funcionalidad y la complejidad y el tiempo que implica la creación de pruebas unitarias valiosas.
Las razones comunes que citan los desarrolladores que limitan la eficiencia de las pruebas unitarias como práctica de desarrollo central incluyen las siguientes.
- Es difícil entender, inicializar y/o aislar las dependencias de la unidad bajo prueba.
- Determinar qué validar y definir las afirmaciones apropiadas requiere mucho tiempo y, a menudo, requiere conjeturas inteligentes.
- Hay mucha codificación manual involucrada, a menudo incluso más de la necesaria para implementar una característica o mejora específica.
- Simplemente no es tan interesante. Los desarrolladores no quieren sentirse probadores. Quieren dedicar tiempo a ofrecer más funciones.
Las herramientas de automatización de pruebas unitarias admiten universalmente algún tipo de marco de prueba, que proporciona la infraestructura de aprovechamiento para ejecutar unidades de forma aislada mientras satisface las dependencias mediante stubs. Esto incluye la generación automatizada de arneses de prueba y los componentes ejecutables necesarios para las pruebas basadas en host y destino.
Sin embargo, la generación y gestión de datos de prueba es el mayor desafío en las pruebas unitarias y la generación de pruebas. Los casos de prueba deben cubrir una gama de funciones de validación, como garantizar requisitos funcionales, detectar comportamientos impredecibles y garantizar la seguridad y los requisitos de protección. Todo ello mientras se satisfacen los criterios de cobertura de la prueba.
La generación de pruebas automatizadas disminuye las ineficiencias de las pruebas unitarias al eliminar las dificultades con la inicialización, el aislamiento y la gestión de dependencias. También elimina gran parte de la codificación manual requerida y al mismo tiempo ayuda a administrar los datos de prueba necesarios para impulsar la verificación y validación.
Análisis estático en sistemas integrados
El análisis de código estático proporciona una variedad de métricas que ayudan a evaluar diferentes aspectos de la seguridad, la confiabilidad, el rendimiento y la mantenibilidad del código integrado. Cada uno de ellos ofrece información valiosa sobre diversos aspectos del estado del código. Una de estas métricas es la complejidad ciclomática, que mide el número de rutas linealmente independientes a través del código fuente de un programa.
Una mayor complejidad indica un mayor potencial de errores y más difícil de leer, mantener y probar. Otros valores que puede proporcionar el análisis estático y que no son medidas de calidad en sí mismos, pero que dan una sensación de mantenibilidad, incluyen:
- Número total de líneas de código.
- Densidad de comentarios
- Acoplamiento o cohesión de clases
- Longitud de la función
Las métricas interesantes que el análisis estático puede proporcionar pero que generalmente no se aplican incluyen la cobertura del código y las métricas de rendimiento. Aunque la cobertura del código suele capturarse mediante pruebas unitarias, el análisis estático puede proporcionar esta métrica e incluso encontrar código inactivo. La métrica de rendimiento se mide identificando el código que puede provocar problemas de rendimiento, como bucles ineficientes o llamadas recursivas. Existen muchas otras métricas sobre el uso de recursos, la duplicación, el puntero nulo y la división por cero.
Al aprovechar estas métricas proporcionadas, el análisis de código estático ayuda a los desarrolladores a identificar problemas potenciales en las primeras etapas del ciclo de desarrollo, mejorar la calidad y el mantenimiento del código y garantizar el cumplimiento de los estándares y las mejores prácticas de codificación.
Pruebas unitarias en sistemas integrados
La verificación y validación del software es una parte inherente del desarrollo de software integrado, y las pruebas son una forma clave de demostrar el comportamiento correcto del software. Las pruebas unitarias son la verificación del diseño del módulo. Garantiza que cada unidad de software haga lo que debe hacer.
Además, los requisitos de seguridad y protección pueden requerir que las unidades de software no se comporten de manera inesperada y no sean susceptibles a la manipulación con entradas de datos inesperadas.
En términos del modelo de desarrollo clásico V, la ejecución de pruebas unitarias es una práctica de validación para garantizar que el diseño del módulo sea correcto. Muchas normas de desarrollo específicas de seguridad tienen pautas sobre lo que se debe probar para las pruebas unitarias. Por ejemplo, ISO 61502 y las normas relacionadas tienen directrices específicas para las pruebas de acuerdo con el nivel de integridad de la seguridad, donde se recomiendan encarecidamente las pruebas basadas en requisitos y las pruebas de interfaz para todos los niveles. Las pruebas de inyección de fallas y uso de recursos se recomiendan en niveles de integridad más bajos y muy recomendables en los niveles más altos de SIL (Niveles de integridad de seguridad). Del mismo modo, el método de conducción de los casos de prueba también se especifica con las prácticas recomendadas.
Ejecución de pruebas automatizada
La automatización de pruebas proporciona grandes beneficios al software integrado. Alejarse de los conjuntos de pruebas que requieren mucha intervención manual significa que las pruebas se pueden realizar de forma más rápida, sencilla y con mayor frecuencia. La descarga de este esfuerzo de prueba manual libera tiempo para una mejor cobertura de las pruebas y otros objetivos de seguridad y calidad. Un requisito importante para la ejecución automatizada de un conjunto de pruebas es poder ejecutar estas pruebas tanto en el entorno de host como en el de destino.
Pruebas basadas en objetivos para sistemas integrados
Automatizar las pruebas de software integrado es más desafiante debido a la complejidad de iniciar y observar pruebas en el hardware de destino. Sin mencionar el acceso limitado al hardware de destino que tienen los equipos de software.
La automatización de las pruebas de software es esencial para que las pruebas integradas sean viables de forma continua desde el sistema de desarrollo anfitrión hasta el sistema de destino. Probar el software integrado requiere mucho tiempo. La automatización del conjunto de pruebas de regresión proporciona un ahorro considerable de tiempo y costes. Además, los resultados de las pruebas y la recopilación de datos de cobertura del código del sistema de destino son esenciales para la validación y el cumplimiento de los estándares.
Se debe registrar y mantener la trazabilidad entre los casos de prueba, los resultados de las pruebas, el código fuente y los requisitos. Por tanto, la recopilación de datos es fundamental en la ejecución de la prueba.
Cobertura del código estructural
Recopilar y analizar métricas de cobertura de código es un aspecto importante del desarrollo de software crítico para la seguridad. La cobertura del código mide la finalización de los casos de prueba y las pruebas ejecutadas. Proporciona evidencia de que la validación está completa, al menos según lo especificado por el diseño del software. También identifica el código muerto. Este es un código que lógicamente nunca se puede alcanzar. Demuestra la ausencia de comportamiento no intencionado. El código que no está cubierto por ninguna prueba es un riesgo porque se desconoce su comportamiento y funcionalidad.
La cantidad y extensión de la cobertura del código depende del nivel de integridad de la seguridad. Cuanto mayor sea el nivel de integridad, mayor será el rigor utilizado e, inevitablemente, el número y la complejidad de los casos de prueba. Independientemente del nivel de cobertura requerido, la generación automatizada de casos de prueba puede aumentar la cobertura de las pruebas con el tiempo.
Las herramientas avanzadas de automatización de pruebas unitarias deben medir estas métricas de cobertura de código. Además, es necesario que esta recopilación de datos funcione en las pruebas de host y destino y acumule el historial de cobertura de pruebas a lo largo del tiempo. Este historial de cobertura de código puede abarcar pruebas unitarias, de integración y de sistemas para garantizar que la cobertura sea completa y rastreable en todos los niveles de prueba.
Tipos de generación automatizada de casos de prueba
A efectos prácticos, las herramientas automatizadas deberían generar casos de prueba en formatos conocidos existentes como CppUnit. De forma predeterminada, tiene sentido un conjunto de pruebas por archivo fuente/encabezado, pero las herramientas deben admitir un conjunto de pruebas por función o un conjunto de pruebas por archivo fuente si es necesario.
Otra consideración importante son las definiciones de códigos auxiliares automáticos para reemplazar funciones "peligrosas", que incluyen rutinas de E/S del sistema como rmdir(), remove(), rename(), etc. Además, se pueden generar automáticamente resguardos para funciones faltantes y definiciones de variables. Se pueden agregar resguardos definidos por el usuario según sea necesario.
Generación de casos de prueba basada en requisitos
Aunque las herramientas de automatización de pruebas no pueden derivar pruebas de requisitos a partir de la documentación, pueden ayudar a que la creación de casos de prueba, resguardos y simulacros sea más fácil y eficiente. Además, la automatización mejora enormemente la gestión de datos de casos de prueba y el soporte de herramientas para pruebas parametrizadas también reduce el esfuerzo manual.
Particularmente importante es la trazabilidad desde los requisitos hasta el código, las pruebas y los resultados de las pruebas. Gestionar la trazabilidad manualmente es casi imposible y la automatización hace que la trazabilidad doble sea una realidad.
Mientras se descomponen los requisitos, la trazabilidad debe mantenerse a lo largo de las fases de desarrollo a medida que los requisitos del cliente se descomponen en requisitos de sistema, de alto nivel y de bajo nivel. La fase de codificación o implementación se da cuenta de los requisitos de bajo nivel. Considere el diagrama V típico del software.
Cada fase impulsa la fase siguiente. A su vez, los elementos de trabajo o los requisitos refinados en cada fase deben satisfacer los requisitos de la fase anterior. Los requisitos arquitectónicos que se han creado o descompuesto a partir del diseño del sistema deben satisfacer los requisitos / diseño del sistema, etc.
La trazabilidad demuestra que cada fase satisface los requisitos de cada fase posterior.
Los desarrolladores escriben código que implementa o cumple cada requisito y, para aplicaciones críticas para la seguridad, se establecen enlaces para la trazabilidad hasta los casos de prueba y hasta el código. Por lo tanto, si un requisito del cliente cambia o se elimina, el equipo sabe qué impacto tendrá en el futuro, hasta el código y las pruebas que validan los requisitos.
Los estándares de la industria como DO-178B/C, ISO 26262, IEC 62304, IEC 61508, EN 50128 y otros requieren la construcción de una matriz de trazabilidad para identificar cualquier brecha en el diseño y verificación de requisitos. Esto ayuda a lograr el objetivo final de crear el producto adecuado. Más que eso, es garantizar que el producto tenga la calidad y la seguridad necesarias para garantizar que siga siendo el producto correcto.
Generación de casos de prueba basada en cobertura de código
La creación de pruebas unitarias productivas siempre ha sido un desafío. El cumplimiento de los estándares de seguridad funcional exige software de alta calidad, lo que genera la necesidad de conjuntos de pruebas que afecten y produzcan estadísticas de alta cobertura de código. Los equipos requieren casos de prueba unitaria que les ayuden a lograr una cobertura de código del 100 %. Esto es más fácil dicho que hecho. Analizar ramas en el código y tratar de encontrar razones por las cuales ciertas secciones del código no están cubiertas continúa robando ciclos a los equipos de desarrollo.
Las herramientas de automatización de pruebas unitarias se pueden utilizar para llenar los vacíos de cobertura en los conjuntos de pruebas. Por ejemplo, el análisis de código estático avanzado (análisis de flujo de control y datos) se utiliza para encontrar valores para los parámetros de entrada necesarios para ejecutar líneas específicas de código descubierto.
También es valioso si tiene herramientas automatizadas que no solo miden la cobertura del código sino que también realizan un seguimiento de cuánto código modificado cubren las pruebas, porque esto puede proporcionar visibilidad sobre si se están escribiendo suficientes pruebas junto con los cambios en el código de producción. Consulte el siguiente informe de cobertura de código de ejemplo.
Uso del análisis de código para impulsar casos de prueba basados en cobertura
En el código complejo, siempre existen declaraciones de código esquivas de las que es extremadamente difícil obtener cobertura. Es probable que haya múltiples valores de entrada con varias permutaciones y posibles rutas que hacen que descifrarlo sea complicado y lleve mucho tiempo. Pero sólo una combinación puede brindarle la cobertura que necesita. La combinación de la automatización de pruebas y el análisis estático facilita la obtención de cobertura de aquellas líneas de código difíciles de alcanzar. En el Asesor de cobertura se muestra un ejemplo de condiciones previas de prueba calculadas con análisis de código.
Generación de casos de prueba de defectos
Otra clase de prueba son aquellas creadas para inducir una condición de error en la unidad bajo prueba. Los parámetros de entrada en estos casos suelen estar fuera de los límites y se encuentran justo en las condiciones límite para los tipos de datos, como el uso de los enteros positivos y negativos más altos de 32 bits para los datos de prueba. Otros ejemplos son las pruebas difusas en las que estas condiciones límite se mezclan con datos aleatorios diseñados para crear una condición de error o desencadenar una vulnerabilidad de seguridad.
Estos casos de prueba validan requisitos no funcionales ya que quedan fuera del alcance de los requisitos del producto, pero son esenciales para determinar el rendimiento, la seguridad, la confiabilidad y otras cualidades del producto. La automatización es esencial ya que estas pruebas pueden ser numerosas (pruebas difusas) y depender de la ejecución repetida (pruebas de rendimiento) para ayudar a descubrir problemas de calidad. La generación de casos de prueba ayuda a reducir el esfuerzo manual necesario para crear estos conjuntos de pruebas.
Pruebas de regresión
Como parte de la mayoría de los procesos de desarrollo de software, las pruebas de regresión se realizan después de realizar cambios en el software. Estas pruebas determinan si los nuevos cambios tuvieron un impacto en el funcionamiento existente del software. La gestión y ejecución de pruebas de regresión suponen una gran parte del esfuerzo y el coste de las pruebas. Incluso con la generación automatizada de pruebas, su ejecución, recopilación de resultados y repetición de pruebas consume mucho tiempo. Las pruebas de regresión abarcan el mantenimiento de casos de prueba, mejoras en la cobertura del código y trazabilidad.
Las pruebas de regresión son necesarias, pero sólo indican que los cambios recientes en el código no han provocado que las pruebas fallen. No hay garantía de que estos cambios funcionen. Además, la naturaleza de los cambios que motivan la necesidad de realizar pruebas de regresión pueden ir más allá de la aplicación actual e incluir cambios en el hardware, el sistema operativo y el entorno operativo.
De hecho, es posible que sea necesario ejecutar todos los casos de prueba creados previamente para garantizar que no existan regresiones y que se construya una nueva versión de software confiable. Esto es fundamental porque se construye o desarrolla cada nuevo sistema de software o versión de subsistema. Si no tienes una base sólida, todo puede colapsar.
Para evitar esto, es importante crear líneas de base de pruebas de regresión que sean una colección organizada de pruebas y que verifiquen automáticamente todos los resultados. Estas pruebas se ejecutan automáticamente de forma regular para verificar si las modificaciones del código cambian o interrumpen la funcionalidad capturada en las pruebas de regresión. Si se introduce algún cambio, estos casos de prueba no alertarán al equipo sobre el problema. Durante las pruebas posteriores, la prueba de Parasoft C++ informará las tareas si detecta cambios en el comportamiento capturado en la prueba inicial.
Cómo decidir qué probar
El desafío clave con las pruebas de regresión es determinar qué partes de una aplicación probar. Es común ejecutar por defecto todas las pruebas de regresión cuando hay dudas sobre los impactos que han tenido los cambios recientes en el código: el enfoque de todo o nada.
Para grandes proyectos de software, esto se convierte en una tarea enorme y reduce la productividad del equipo. Esta incapacidad para centrar las pruebas obstaculiza muchos de los beneficios de los procesos iterativos y continuos, lo que puede verse exacerbado en el software integrado, donde los objetivos de las pruebas son un recurso limitado.
Aquí se requieren un par de tareas.
- Identifique qué pruebas deben volver a ejecutarse.
- Enfoque los esfuerzos de prueba (pruebas unitarias, pruebas funcionales automatizadas y pruebas manuales) en validar las funciones y el código relacionado afectados por los cambios más recientes.
Análisis de impacto de prueba
El análisis de impacto de prueba (TIA) utiliza datos recopilados durante las ejecuciones de prueba y los cambios en el código entre compilaciones para determinar qué archivos han cambiado y qué pruebas específicas tocaron esos archivos. El motor de análisis de Parasoft puede analizar el delta entre dos compilaciones e identificar el subconjunto de pruebas de regresión que deben ejecutarse. También comprende las dependencias de las unidades modificadas para determinar qué efecto dominó han tenido los cambios en otras unidades.
Centrarse en el riesgo
Debido a la complejidad de las bases de código actuales, cada cambio de código, por inocuo que sea, puede afectar sutilmente la estabilidad de la aplicación y, en última instancia, "romper el sistema". Estas consecuencias no deseadas son imposibles de descubrir mediante una inspección manual, por lo que las pruebas son fundamentales para mitigar el riesgo que representan. A menos que se entienda qué es lo que se necesita volver a descansar, no se puede lograr una práctica de prueba eficiente. Si hay demasiadas pruebas en cada sprint o iteración, se reduce la eficiencia que aporta la automatización de pruebas. Probar muy poco no es una opción.
El mejor enfoque es identificar qué pruebas deben volver a ejecutarse y centrar los esfuerzos de prueba (pruebas unitarias, pruebas funcionales automatizadas y pruebas manuales) en validar las características y el código relacionado que se ven afectados por los cambios más recientes.
Esto se descubre con TIA y la planificación de pruebas basadas en un enfoque basado en datos llamado prueba basada en cambios.
TIA necesita un repositorio de pruebas ya completadas que ya se hayan ejecutado en cada compilación, ya sea como parte de un proceso de prueba totalmente automatizado (como un paso de compilación impulsado por CI) o mientras se prueba la nueva funcionalidad. Este análisis proporciona información sobre en qué parte del código se produjeron los cambios, cómo se correlacionan las pruebas existentes con esos cambios y dónde deben centrarse los recursos de prueba. A continuación se muestra un ejemplo de TIA.
A partir de aquí, el plan de pruebas de regresión se amplía para abordar los casos de prueba fallidos e incompletos con la máxima prioridad y utilizar las recomendaciones de repetición de pruebas para centrar la programación de ejecuciones automatizadas adicionales y priorizar los esfuerzos de pruebas manuales.
Las pruebas son un cuello de botella importante para el desarrollo de software integrado, ya que se identifican demasiados defectos al final del ciclo de lanzamiento debido a pruebas insuficientes o mal dirigidas. Para obtener los mejores resultados, centre los esfuerzos de prueba en el impacto de los cambios que se realizan para desbloquear la eficiencia que ofrece la automatización de pruebas.
Smiths Medical utiliza la generación de pruebas automatizada para una entrega segura y de alta calidad
Cumplimiento satisfecho
Cumplí con los requisitos de análisis estático, pruebas unitarias y cobertura de código.
Simplificado
Cualificación de herramientas.
Resum
Las pruebas son esenciales para el desarrollo de software integrado. Fomenta una verdadera comprensión del código que se está desarrollando y proporciona una base sólida para un régimen de verificación y validación necesario para satisfacer los objetivos de seguridad de un producto.
El análisis estático es una técnica crucial en el desarrollo de software que implica examinar el código fuente sin ejecutarlo para identificar problemas potenciales y mejorar la calidad general del código. Cuando los equipos lo usan desde el principio, el análisis estático:
- Proporciona información valiosa sobre la complejidad del código, la capacidad de mantenimiento, las vulnerabilidades de seguridad y el cumplimiento de los estándares de codificación.
- Ayuda a prevenir errores.
- Optimiza el rendimiento.
- Garantiza el cumplimiento de las mejores prácticas y requisitos reglamentarios.
La integración del análisis estático en el flujo de trabajo de desarrollo conduce a una base de código más sólida y fácil de mantener. Esto debe hacerse junto con las pruebas unitarias para que no se considere un freno a la productividad.
Las limitaciones en la productividad de las pruebas se deben a múltiples factores, como la presión y el tiempo que lleva ofrecer una mayor funcionalidad, y la complejidad y el tiempo que implica crear pruebas valiosas.
La generación y gestión de datos de prueba es, con diferencia, el mayor desafío en las pruebas unitarias y la generación de pruebas. Los casos de prueba son particularmente importantes en el desarrollo de software crítico para la seguridad porque deben garantizar los requisitos funcionales y probar el comportamiento impredecible, la seguridad y los requisitos de protección. Todo ello mientras se satisfacen los criterios de cobertura de la prueba.
La generación de pruebas automatizadas disminuye las ineficiencias de las pruebas unitarias al eliminar las dificultades con la inicialización, el aislamiento y la gestión de dependencias. También elimina gran parte de la codificación manual requerida y al mismo tiempo ayuda a administrar los datos de prueba necesarios para impulsar la verificación y validación. Esto mejora la calidad, la seguridad y la protección. También reduce el tiempo de prueba, los costos y el tiempo de comercialización.