Para aquellos que estén interesados en el tema de la automatización en iOS, tengo dos novedades: buenas y malas. Bien: en la aplicación iOS para servicios pagos, solo se utiliza un punto de integración:
compras en la aplicación (compras
integradas en la aplicación ). Malo: Apple no proporciona ninguna herramienta para automatizar las compras de prueba.
En este artículo, sugiero que usted y yo busquemos un método de automatización universal más allá del bien y el mal de Apple. El artículo será útil para cualquier persona que integre servicios de terceros que sean una caja negra en sus aplicaciones: publicidad, transmisión, administración de ubicación, etc. Por lo general, tales integraciones son muy difíciles de probar, ya que no hay forma de configurar de manera flexible un servicio de terceros para probar la aplicación.
Mi nombre es Victor Koronevich, soy ingeniero senior de automatización de pruebas en Badoo. Comprometido en la automatización móvil durante más de diez años. Junto con mi colega Vladimir Solodov, hicimos este informe en la conferencia de Heisenbug. También me ayudó a preparar este texto.En un
artículo anterior
, describimos qué métodos utiliza Badoo para probar la integración con los proveedores de pagos, que tenemos más de 70. En este artículo hablaremos más sobre cómo logramos lograr una automatización estable y económica de probar servicios pagos en una aplicación iOS.
Comencemos con una descripción general de nuestra investigación:
- Definición del problema
- Declaración del problema.
- Solución No. 1. Sandbox de manzana
- Decisión número 2. Método de simulación de funciones y uso de un objeto falso
- Valoración de la decisión: principales riesgos
- Resultado
- Conclusión
Definición del problema
La automatización debe hacerse cuando surge una necesidad natural. ¿Cuándo llegó este momento con nosotros?
Hay muchas funciones gratuitas en la aplicación Badoo, pero las pagas le dan al usuario más opciones. Los obtienen de dos maneras: para préstamos, la moneda interna de Badoo, o mediante la compra de una suscripción premium. Para un cierto número de créditos, puede elevar su perfil en los resultados de búsqueda al primer lugar, hacer un regalo a otro usuario y más. La suscripción premium es válida por un cierto período de tiempo y ofrece varias opciones a la vez: activar el modo de invisibilidad, ver a las personas que han mostrado simpatía por usted, cancelar el resultado de su voto y otros.
Estas características aparecieron en Badoo gradualmente. Y hace un par de años, probamos los servicios pagos en aplicaciones iOS solo manualmente. Pero a medida que aparecen las funciones y las nuevas pantallas, las pruebas manuales tomaron más y más tiempo. Los requisitos para los cambios en la aplicación provienen de diferentes lados: desde los desarrolladores del lado del cliente, los desarrolladores del lado del servidor e incluso el propio proveedor de Apple. Para un probador, una iteración de prueba comenzó a tomar alrededor de ocho horas. Se hizo imposible obtener una respuesta rápida para un desarrollador en su sucursal en 30 minutos, lo que finalmente podría afectar negativamente la competitividad del producto.
Queríamos obtener los resultados de las pruebas lo más rápido posible. Y se encontraron con un problema: ¿cómo organizar las pruebas de regresión de los servicios pagos en nuestras aplicaciones iOS de forma económica para obtener resultados rápidos y estables?
Declaración del problema.
Entonces, teniendo en cuenta los detalles de nuestro proceso de entrega del producto final y el tamaño del equipo, queremos:
- Probar cualquier compra dentro de la aplicación del cliente (pagos únicos y suscripciones);
- repetir iteraciones de prueba 10–20 veces al día;
- Obtenga resultados de prueba ~ 150 scripts de prueba en menos de media hora;
- deshacerse del ruido;
- poder ejecutar pruebas en una rama específica del código de desarrollador, independientemente de los resultados de otras ejecuciones.
Ahora que hemos formulado la tarea, es hora de comenzar el viaje al maravilloso mundo de los ingenieros y sus soluciones.
Solución No. 1. Sandbox de manzana
En primer lugar, comenzamos a buscar información sobre la organización de pruebas automáticas de servicios pagos en la documentación de Apple. Y no encontraron nada. El soporte de automatización se ve muy escaso. Si aparece algo, entonces la configuración de la automatización con las herramientas propuestas es difícil (recordemos al menos la
automatización UIA , así como el momento en que
apareció la primera utilidad
xcrun simctl para iOS Simulator) y hay que buscar soluciones de ingeniería, incluso en el segmento de código abierto.
En la documentación de Apple para probar servicios pagos, solo puede encontrar
Apple Sandbox . No estaba claro cómo vincular este entorno limitado a la automatización, pero decidimos investigar seriamente esta solución. El hecho de que el entorno limitado de Android fuera estable nos dio confianza, y para entonces ya habíamos escrito con éxito las pruebas en Android. ¿Quizás el sandbox de Apple sea igual de bueno?
Pero cuando implementamos las pruebas automáticas usando este sandbox, bebimos por completo. Repasemos rápidamente los principales problemas.
1. El grupo de usuarios de prueba
La principal limitación para la automatización fueron las características del contenido en el grupo de usuarios de prueba, lo que debería garantizar la independencia del lanzamiento de las pruebas automáticas.
Para ejecutar solo una compra automática de una compra de suscripción, necesitamos:
- tomar un nuevo usuario para autorización en el sandbox;
- cambiar en el simulador la ID de Apple vinculada actual;
- Inicie sesión en Badoo con Badoo
- ir a la pantalla de compra de suscripción y seleccionar un producto;
- Confirme la compra e inicie sesión con Apple ID;
- asegúrese de que la compra haya sido exitosa;
- enviar usuario de Badoo para limpieza;
- borre al usuario del sandbox de las suscripciones.
Si intenta utilizar inmediatamente al mismo usuario en la próxima prueba, será imposible comprar una segunda suscripción. Debe esperar hasta que la primera suscripción "salga mal" o cancelar la suscripción en la configuración. Como dijimos en el primer
artículo , el sandbox tiene un período de validez de suscripción específico. Si compra una suscripción "por un mes", debe esperar cinco minutos para cerrarla automáticamente. El proceso de cancelación de la suscripción tampoco es rápido.
En consecuencia, para una nueva ejecución de la misma prueba, tendremos que esperar hasta que finalice la suscripción o tomar otro usuario "limpio". Si queremos ejecutar dos pruebas simultáneamente independientemente una de la otra, entonces necesitamos tener al menos dos usuarios de sandbox en el grupo. Por lo tanto, para ejecutar 100 pruebas automáticas en paralelo en 100 hilos, necesitamos 100 usuarios diferentes.
Y ahora imaginemos que estamos realizando una prueba de autoevaluación en dos agentes, cada uno de los cuales puede ejecutarlos en 100 subprocesos. ¡En este caso, necesitamos al menos 200 usuarios!
2. Notificaciones "malas"
Bueno, ¡qué demonios no es broma! Organizamos un grupo de usuarios y comenzamos a observar cómo se ejecutan las pruebas. Cayeron por el camino, pero la mayoría, por nuevas razones desconocidas para nosotros. Comenzamos a comprender y nos dimos cuenta de que al autorizar, confirmar una compra y trabajar como usuario en el sandbox, la App Store envía alertas: por ejemplo, solicita un nuevo nombre de usuario y contraseña, confirma la autorización haciendo clic en el botón "Aceptar", brinda información sobre un error interno con el botón "Aceptar" . A veces aparecen, a veces no. Y si aparecen, siempre en un orden diferente.

¿Cómo es posible que un error sospechoso simplemente se ignore en una prueba automática? Y si llega un error real, ¿qué debo hacer? Esta área se convirtió automáticamente en una "zona ciega" para nosotros, y tuvimos que escribir controladores especiales para todas las alertas posibles que pudieran llegar desde la App Store.
Todo esto hizo que las pruebas fueran más lentas:
- las alertas podrían llegar a diferentes pasos del escenario de prueba, destruyendo la idea principal de la prueba: escenario de prueba predecible; tuvimos que agregar un controlador de errores que esperaba que apareciera una posible serie de alertas ignoradas conocidas;
- a veces llegaron nuevas variaciones de alertas u ocurrieron otros errores, por lo que tuvimos que reiniciar las pruebas caídas; Esto aumentó el tiempo de ejecución de todas las pruebas.
3. ¿Hubo una prueba?
Por lo tanto, los usuarios del grupo se bloquean y luego se borran durante n minutos. Realizamos pruebas en 120 subprocesos y ya hay muchos usuarios en el grupo, pero esto no es suficiente. Creamos nuestro sistema de gestión de usuarios, creamos un controlador de alertas, y luego sucedió la TI. El sandbox no estará disponible durante un par de días para cualquier usuario de prueba.
Nadie esperaba esto. Y esta fue la gota que colmó el vaso de nuestra paciencia, que finalmente mató el amor de la caja de arena de Apple y nos hizo emprender el camino más allá del bien y el mal. Nos dimos cuenta de que no necesitábamos tal automatización y que no queríamos sufrir más con esta peligrosa decisión.
Decisión número 2. Método de simulación de funciones y uso de un objeto falso
Entonces, bebimos problemas con la automatización en el sandbox de Apple. Pero no pienses que en el mundo móvil todo es completamente malo. En Android, el sandbox es mucho más estable: puede ejecutar pruebas automáticas allí.
Intentemos encontrar otra solución para iOS. ¿Pero cómo mirar? Donde mirar Veamos la historia de las pruebas y el desarrollo de software: ¿qué pasó con el loco mundo de Apple? ¿Qué dicen las personas que han escrito un montón de libros y se han ganado la autoridad en el mundo de la automatización y el desarrollo de software?
Inmediatamente recordé el trabajo "Patrones de prueba xUnit: Refactoring Test Code", escrito por Gerard Mesaroche (
revisión de Martin Fowler), - en mi opinión, uno de los mejores libros para cualquier probador que conozca al menos un lenguaje de programación de alto nivel y quiera hacer la automatización . Un par de capítulos de este libro dedicados a probar SUT en forma aislada de otros componentes de la aplicación, que son nuestro "recuadro negro", pueden ayudarnos.
1. Introducción a mocha y falso
Cabe señalar que en el mundo de las pruebas automáticas no existe un límite generalmente aceptado entre los conceptos de Dobles de prueba, Test Stub, Test Spy, Mock Object, Fake Object, Dummy Object. Siempre debe considerar la terminología del autor. Solo necesitamos dos conceptos del gran mundo de Test Doubles: una función simulada y un objeto falso. Que es esto ¿Y por qué necesitamos esto? Damos una breve definición de estos conceptos para que no tengamos desacuerdos.
Supongamos que tenemos una aplicación y un componente integrado, que es para nosotros una "caja negra". Dentro de la aplicación, podemos llamar a las funciones accediendo a este componente y obtener los resultados de estas funciones. Dependiendo del resultado, nuestra aplicación reacciona de manera específica. A veces, el resultado de la ejecución de la función puede ser una entidad completa con un montón de campos que reflejan los datos reales del usuario.
Sustitución de una función por otra que devuelva el resultado deseado, llamemos simulacro de la función, o simplemente simulacro. Estas funciones pueden tener la misma firma, pero estas son dos funciones diferentes.
Y la sustitución de la entidad obtenida como resultado de la función de una entidad falsa (que contiene los datos necesarios en los campos, y a veces incluso datos corruptos) se llamará la implementación de un objeto falso. Puede leer más sobre esto en el libro que mencioné anteriormente, o en cualquier otro compendio para pruebas y desarrollo de software.
Para terminar con esto, enfaticemos algunas características del uso de funciones simuladas y objetos falsos:
- Para que las funciones se mojen, debe acceder al código fuente y saber cómo funciona la aplicación con el componente desde el interior a nivel de desarrollador.
- Para implementar un objeto falso, necesita conocer la estructura del objeto real.
- El uso de la función simulada permite una configuración flexible de la aplicación con el componente.
- El uso de un objeto falso le permite dotar a una entidad de cualquier propiedad.
El método de objetos falsos y moki es ideal para aislar el funcionamiento de un componente dentro de una aplicación. Veamos cómo podemos aplicar este método para resolver nuestro problema, donde App Store será el componente. Debido a las peculiaridades del uso de este método, primero tenemos que pasar a estudiar la naturaleza del trabajo de nuestra aplicación con el componente, y luego a la implementación técnica para hacer mokeys específicos y objetos falsos.
2. ¿Cómo sucede una compra real?
Antes de comenzar a describir la interacción de todas las partes del sistema, destaquemos los principales actores:
- usuario de la aplicación: cualquier actor que realiza acciones con la aplicación, puede ser una persona o un guión que realiza las instrucciones necesarias;
- aplicación (en nuestro caso, utilizamos la aplicación Badoo iOS instalada en el simulador de iOS);
- servidor: un actor que procesa las solicitudes de la aplicación y envía respuestas o notificaciones asincrónicas sin una solicitud del cliente (en este caso, nos referimos a un servidor Badoo abstracto para simplificar la estructura);
- La App Store es un actor que es una "caja negra" para nosotros: no sabemos cómo está organizada en su interior, pero sabemos su interfaz pública para procesar compras dentro de la aplicación ( marco StoreKit ), y también sabemos cómo verificar los datos en un servidor Apple.
Veamos cómo ocurre la compra. Todo el proceso se puede ver en el diagrama:
Figura 1. Esquema de pago en App StoreDescribiremos paso a paso las principales acciones de los actores.
1. El punto de partida es el estado de todos los actores antes de abrir la pantalla con una lista de productos.
¿Qué es esta pantalla y cómo llegamos a ella?
Supongamos que un usuario encuentra a una persona interesante, abre su perfil, escribe un mensaje y desea enviar un regalo. Enviar un regalo es un servicio pago. El usuario puede desplazar el perfil a la sección para enviar regalos o seleccionar inmediatamente un regalo del chat.
Si el usuario selecciona un regalo y no tiene dinero en la cuenta, verá una lista de diferentes paquetes de préstamos (Asistente de pagos) para la compra. El punto de partida en nuestro ejemplo es una lista de regalos. En el diagrama, podemos considerar este punto en cualquier pantalla antes de mostrar la lista de productos para la compra de préstamos o suscripción.
2. Abrir una lista de productos.
Estamos en el punto de partida, por ejemplo, en la lista de regalos. El usuario selecciona uno de los regalos en la aplicación. La aplicación realiza una solicitud a nuestro servidor para obtener una lista de posibles paquetes de préstamos de identificación de producto (100, 550, 2000, 5000). El servidor devuelve esta lista a la aplicación.
A continuación, la aplicación envía la lista de ID de producto recibida para su verificación al actor de App Store (marco iOS del sistema StoreKit que va al servidor de Apple). Devuelve una lista de productos probados y, como resultado, la aplicación muestra al usuario la lista final de paquetes de préstamos con íconos y precios.
3. Selección de productos y generación de recibos.
El usuario selecciona un producto pagado. La App Store requiere comprobante de compra y autorización a través de Apple ID. Después de una autorización de usuario exitosa, el control se transfiere a la aplicación. La aplicación está esperando que se genere un recibo dentro de su propio paquete. El usuario en este momento ve el sol, que bloquea la pantalla. Ese recibo generado se puede entender utilizando el método appStoreReceiptURL de la clase
Bundle . Una vez que App Store genera el cheque, la aplicación selecciona el cheque de su paquete y envía una solicitud con el cheque y los datos del usuario al servidor Badoo.
4. Comprobación de la comprobación en el servidor Badoo.
Tan pronto como el servidor Badoo recibe la verificación y los datos del usuario, los envía de vuelta al lado del servidor Apple para llevar a cabo el primer ciclo de verificación. Esta es una de las recomendaciones de Apple. Luego, en este primer ciclo de verificación, el servidor recibe información sobre el estado actual de la suscripción.
5. Envío de notificaciones push (notificación push) desde el servidor.
El servidor Badoo procesa nuevamente la información recibida después de la verificación por parte de Apple y envía a la aplicación una respuesta junto con una notificación push.
6. Notificación de inserción en la aplicación.
Si se trataba de una compra de préstamos, inmediatamente cambiará el saldo del usuario en la aplicación y verá el regalo enviado en el chat. Si se trata de una compra por suscripción, el usuario debe esperar la notificación push final de que la suscripción está activada.
3. Determinación de dependencias y bucle de prueba.
Para una mayor discusión, presentamos dos conceptos más: la dependencia externa y el circuito de prueba.
Dependencia externa
Por dependencias externas nos referimos a cualquier interacción con un componente, que es para nosotros un "recuadro negro". En este caso, la App Store actúa como un componente en forma de un marco de sistema iOS (StoreKit), con el que funciona nuestra aplicación iOS, y un servidor Apple, donde van las solicitudes de verificación.
La gestión de estas dependencias en condiciones reales es imposible, la aplicación se ve obligada a responder a las señales de salida del cuadro negro (ver Fig. 2).
Tenemos tres dependencias externas:
- Comprobación de productos StoreKit.
- Recibir y reemplazar un recibo de compra.
- Comprobación de una comprobación en un servidor Badoo.
Figura 2. Dependencias externasCircuito de prueba
Circuito de prueba: estas son secciones de la ruta que recorreremos y verificaremos durante el proceso de prueba.
Figura 3. Bucle de pruebaEl objetivo de nuestro trabajo para eliminar las dependencias es construir un circuito de prueba que esté lo más cerca posible de la ruta real y le permita excluir todas las dependencias externas y transferir el control a su lado.
Consideramos cada dependencia en secuencia.
4. Aislamiento de dependencias: implementación técnica
En nuestra empresa, para la implementación de los pagos, se tomó un concepto de APP, que se basa en la interfaz del Proveedor de pagos. Esta es la interfaz principal para interactuar con el actor de App Store (StoreKit) dentro de nuestra aplicación, que tiene dos métodos principales:
- preparar es el método responsable de verificar los productos;
- makePayment es un método que procesa una compra en la aplicación.
Todos los pagos en iOS se refactorizaron de acuerdo con este concepto, lo que nos permitió obtener un proveedor de pagos simulados de clase simple y conveniente. Esta es la interfaz principal para interactuar con una copia conveniente del comportamiento de StoreKit dentro de nuestra aplicación. ¿Qué significa "copia conveniente"? Este proveedor tiene simulacros de los métodos prepare y makePayment que hacen lo que queremos. Veamos un ejemplo de piezas de código, cómo logramos integrar moki.
Dependencia No. 1. Comprobación de productos StoreKit
Para verificar la lista de productos, use la función de preparación, que devuelve una lista de productos verificados. Podemos utilizar el simulacro en el que apagamos el cheque y devolvemos la lista entrante de productos como totalmente verificada. Por lo tanto, se eliminará la dependencia.
Figura 4. El primer esquema de eliminación de dependenciasEn la parte superior de la arquitectura en nuestra aplicación está el Proveedor de pagos. Refleja la interfaz de un posible proveedor en la aplicación. El código para implementar mok se puede encontrar en la clase Mock Payment Provider.
public class MockPaymentProvider: PaymentProvider { public static var receipt: String? public static var storeKitTransactionID: String? public func prepare(products: [BMProduct]) -> [BMProduct] { return products } ... }
Listado 1. Verificación de cliente simuladoEn el proveedor de pagos simulados, podemos ver la implementación del método de preparación. La magia de moka resulta ser muy simple: el método omitió la comprobación de productos en el lado de StoreKit y simplemente devuelve una lista entrante de productos. La implementación real de prepare se ve así:
public func prepare(products: [BMProduct]) -> [BMProduct] { let validatedProducts = self.productsSource.validate(products: products) return validatedProducts }
Listado 2. Proveedor de pago de la tienda realDependencia No. 2. Recibir y reemplazar un recibo de compra
La segunda dependencia es un poco más complicada: primero debemos eliminar la autorización para no mantener el grupo de cuentas de usuario y, de alguna manera, obtener el cheque en sí. Simplemente podemos eliminar el formulario de autorización:
Figura 5. Eliminar un formulario de autorización al hacer un pagoNo es tan simple con un cheque. Hay muchas preguntas:
- ¿Cómo obtener un recibo por adelantado del producto correcto?
- Si recibimos el cheque, ¿cuándo y cómo adjuntarlo dentro de la aplicación?
Aquí el actor "Usuario" tiene un nuevo rol: control de calidad. Cuando ejecutamos la prueba, no solo podemos hacer clic en los botones de la interfaz, sino también llamar a los métodos API del marco de prueba (métodos que simulan acciones del usuario) y a los servicios REST API (métodos que pueden hacer magia desde el servicio interno de Badoo). En Badoo utilizamos una herramienta API de control de calidad muy potente (puede encontrar todas sus capacidades en el enlace:
https://vimeo.com/116931200 ). Es él quien nos ayuda en las pruebas y realiza una comprobación del producto correcto en el lado del servidor de Badoo. El servidor Badoo es el mejor lugar para generar cheques: hay cifrado y descifrado del cheque, por lo que el servidor sabe todo acerca de esta estructura de datos.
Después de recibir un cheque falso, podemos pasarlo por una
puerta trasera en el lado de la aplicación. A continuación, la aplicación enviará un cheque falso junto con los datos del usuario a nuestro servidor.
Figura 6. Esquema de recepción¿Cómo se hizo esto técnicamente posible?
1. Para configurar un cheque falso en la aplicación, pudimos usar una puerta trasera que guardó el cheque falso en el campo de recibo MockPaymentProvider:
#if BUILD_FOR_AUTOMATION @objc extension BadooAppDelegate { @objc func setMockPurchaseReceipt(_ receipt: String?) { PaymentProvidersFactory.useMockPaymentProviderForITunesPayments = true MockPaymentProvider.receipt = receipt } ... } #endif
Listado 3. Puerta trasera de verificación falsa2. La aplicación pudo tomar nuestro cheque gracias a MockPaymentProvider, en el que utilizamos el simulacro makePayment y el cheque guardado en MockPaymentProvider.receipt:
public class MockPaymentProvider: PaymentProvider { ... public func makePayment(_ transaction: BPDPaymentTransactionContext) { ... if let receiptData = MockPaymentProvider.receipt?.data(using: .utf8) { let request = BPDPurchaseReceiptRequest(...) self.networkService.send(request, completion: { [weak self] (_) in guard let sSelf = self else { return } if let receipt = request.responsePayload() { sSelf.delegate?.paymentProvider(sSelf, didReceiveReceipt: receipt) } }) } else { self.delegate?.paymentProvider(self, didFailTransaction: transaction) } } }
Listado 4. Llamar a un moka de procesamiento de compras con un cheque falso3. Obtener un cheque falso
Para obtener una verificación falsa, utilizamos el método en el servidor (ver Listado 5). Se necesita una matriz predeterminada con datos para generar datos de verificación y agrega los datos necesarios para un producto en particular.
$new_receipt_model = array_replace_recursive(
Listado 5. Parte del servidor de la generación de chequesPara repetir la estructura de una verificación real, la verificación personalizada enviada por la aplicación debe cifrarse con un certificado. Utilizamos nuestro certificado de trabajo en lugar del certificado de Apple.
function signReceipt($receipt, $response) {
Listado 6. Método para firmar un cheque con un certificado4. Como resultado, en la prueba obtenemos:
(/ "((\d+) | (\d+) ?/) do |service_type|
Listado 7. Paso de prueba de pepinillo para el marco de pepinoDependencia No. 3. Comprobación de una comprobación en un servidor Badoo
Para eliminar la tercera dependencia, debe deshacerse de la verificación de verificación en el servidor. Es importante recordar que la verificación se realiza en dos etapas. En la primera etapa, la verificación se autentica en base a firmas y certificados. En el segundo, el cheque se envía a la App Store. En caso de validación exitosa en esta etapa, recibiremos un cheque descifrado que puede procesarse.
Figura 7. Eliminar la verificación del servidorPrimero, el servidor realiza la verificación inicial de la verificación en el método generateReceiptByCert de la clase primaria. Esto verifica la firma con el certificado de App Store. En el caso de una verificación falsa, esta verificación fallará porque está firmada por nuestro certificado, y llamaremos al método para la verificación con el certificado local verificarReceiptByLocalCert. En este método, intentaremos descifrar la verificación con un certificado local y, si tiene éxito, colocaremos el resultado de descifrado en el campo interno local_receipt de la clase secundaria (método addLocallyVerifiedReceipt).
class EngineTest extends Engine function verifyReceiptByCert($receipt) { $result = parent::verifyReceiptByCert($receipt); if ($result === -1 || empty($result)) { $result = $this->verifyReceiptByLocalCert($receipt); } return $result; } function verifyReceiptByLocalCert($receipt) { $receipt_file = tempnam(sys_get_temp_dir(), 'rcp'); file_put_contents($receipt_file, base64_decode($receipt)); $result = openssl_pkcs7_verify($receipt_file, PKCS7_BINARY, '/dev/null', [$DIR]); if ($result) { $this->addLocallyVerifiedReceipt($receipt, base64_decode($response)); } unlink($receipt_file); return $result; } class Engine function verifyReceiptByCert($receipt) { $receipt_file = tempnam(sys_get_temp_dir(), 'rcp'); file_put_contents($receipt_file, base64_decode($receipt)); $result = openssl_pkcs7_verify($receipt_file, PKCS7_BINARY, '/dev/null', [$DIR]); unlink($receipt_file); return $result; }
Listado 8. Verificación inicialDurante la verificación secundaria (generateReceipt), obtenemos el valor del campo local_receipt de la clase secundaria getLocallyVerifiedReceipt. Si no está vacío, usamos su valor como resultado de la verificación.
Si el campo está vacío, llamamos a la verificación secundaria desde la clase
padre (
parent :: verificarReceipt). Allí hacemos una solicitud a la tienda de aplicaciones para su verificación por su parte. El resultado de la verificación en ambos casos es una verificación descifrada.
class EngineTest extends Engine function verifyReceipt($receipt_encoded, $shared_secret, $env) { $response = $this->getLocallyVerifiedReceipt($receipt_encoded); if (!empty($response)) { return json_decode($response, true); } return parent::verifyReceipt($receipt_encoded, $shared_secret, $env); } class Engine function verifyReceipt($receipt_encoded, $shared_secret, $env) { $response = $this->_sendRequest($receipt_encoded, $shared_secret, $env); return $response; }
Listado 9. Verificación secundaria5. Prueba de video: compra de préstamos y suscripciones
Prueba número 1. Compra de suscripción
Video de prueba:

Prueba número 2. Comprar préstamos y enviar un regalo
Video de prueba:

Valoración de la decisión: principales riesgos
Eliminar las dependencias externas conlleva ciertos riesgos.
1. Configuración incorrecta.
Como la verificación no está de nuestro lado, podemos configurar nuestros productos incorrectamente en el lado de Apple. Para protegernos del error, escribimos una prueba de unidad del lado del servidor separada, que verifica que todos los productos que comenzamos en el lado de Apple coinciden con los productos que tenemos en nuestra configuración.
2. Casos límite.
Por ejemplo, cuando el pago se completa por completo, el usuario recibe una notificación de que ha completado, pero nuestra aplicación no puede encontrar el cheque que debe falsificarse como resultado de realizar este pago. El riesgo radica en el hecho de que nosotros mismos adjuntamos el cheque con la ayuda de una puerta trasera, y naturalmente no podemos rastrear tal caso. Para compensar de alguna manera este riesgo, realizamos controles de extremo a extremo utilizando la caja de arena o un pago real después del lanzamiento.
3. Falso o fraude injusto.
Después de leer este artículo, puede pensar que, dado que Badoo usa cheques falsos, puede adjuntarnos algo falso y usar el servicio de forma gratuita. Para que este riesgo no se materialice, firmamos todo con nuestro propio certificado y limitamos el uso de pruebas falsas y falsas a pruebas funcionales que se ejecutan solo en nuestro entorno de desarrollo.
4. Cambie el formato del cheque.
Este es el riesgo más grave. Es posible cambiar el formato de un cheque cuando Apple cambia algo sin avisarnos. Tuvimos ese caso: al cambiar a iOS 11, el formato del cheque cambió por completo. Generamos un cheque falso en nuestro servidor y lo usamos en la prueba. Todo fue perfecto con nosotros: todos los campos están en su lugar, todo es maravilloso, todo se está procesando. Pero cuando cambiamos al sistema real, nada funcionó. Los campos que fueron significativos en el cheque simplemente dejaron de existir.
¿Cómo compensar este riesgo? En primer lugar, no excluimos la posibilidad de realizar pruebas de extremo a extremo del sandbox antes del lanzamiento y el pago real después del lanzamiento. Ahora estamos en la fase activa de un proyecto para verificar las notificaciones, cuando tratamos de clasificar todos los cheques que recibimos de la producción en función de si entendemos qué es o no. Si la respuesta es no, entonces comenzamos a procesar todo manualmente, ver qué ha cambiado, qué está mal, qué hay que cambiar en nuestro sistema.
Resultado
Considere las principales ventajas que pudimos obtener como resultado de aplicar el método moki y el objeto falso.
Automatización económica, rápida y estable de servicios pagos en iOS
Junto con el equipo de prueba manual de iOS (agradecimiento especial a Colin Chan), pudimos escribir más de 150 pruebas automáticas para pagos. Esta es una cantidad bastante grande de cobertura para un área de la aplicación.
Gracias a la paralelización, podemos obtener el resultado en solo 15-20 minutos en cualquier rama del desarrollador del cliente iOS o desarrollador del servidor de facturación. Antes de la automatización, la prueba manual de esta área por una persona tomó ocho horas.
También podemos probar la gran mayoría de los casos de prueba configurando el proveedor de pagos simulados a través de moki de la manera que necesitamos. Con la ayuda de mooks, aprendimos cómo desactivar la verificación del producto y simular casos cuando la verificación se realiza parcialmente. Por lo tanto, abrimos casos que antes no podíamos probar en principio.
Regresión funcional en el desarrollo de nuevas características.
La automatización funcionó muy bien en aquellos casos en que el desarrollador en el proceso de trabajar en una nueva característica afectaba la funcionalidad anterior. Tuvimos un ejemplo cuando un desarrollador hizo una característica compleja con el almacenamiento en caché y ejecutó nuestras pruebas automáticas. Algunos de ellos cayeron en error. Lo vio y lo arregló. Luego reinició las pruebas automáticas nuevamente, y nuevamente, algo cayó. Como resultado, hizo una serie de iteraciones hasta el momento en que todo comenzó a funcionar normalmente en el lado de la aplicación.
Regresión funcional en refactorización de pagos
Quizás la automatización más exitosa y eficiente que sea posible ocurra en el campo de la refactorización de código. En este caso, solo cambia la implementación interna; no es necesario cambiar el código de la prueba automática. La interfaz de usuario no cambia de ninguna manera, y las pruebas automáticas se pueden conducir de manera eficiente.
Prueba de características experimentales de Apple: período de gracia
Un sistema similar es completamente intercambiable cuando prueba nuevas integraciones que aún no están implementadas en el sandbox. Así fue con el período de gracia. Esta funcionalidad no está en el sandbox. El período de gracia en Apple aún no está disponible para todos. Este es un proyecto piloto que Badoo está implementando con Apple. Para verificar con un período de gracia, necesitábamos agregar aquí un fragmento de código JSON:
pending_renewal_info:[ { expiration_intent: 2 grace_period_expires_date: 2019-04-25 15:50:57 Etc/GMT auto_renew_product_id: badoo.productId original_transaction_id: 560000361869085 is_in_billing_retry_period: 1 grace_period_expires_date_pst: 2019-04-25 08:50:57 America/Los_Angeles product_id: badoo.productId grace_period_expires_date_ms: 1556207457000 auto_renew_status: 1 }]
Listado 10. Período de gracia para una suscripciónLo hicimos muy fácilmente en solo unos segundos. En nuestro sistema, pudimos probar nuestra reacción a una nueva característica. Ahora estamos ejecutando esta funcionalidad en el producto.
Pruebas de calidad del producto en métodos de composición.
Como resultado de nuestra investigación, pudimos describir un método que elimina el ruido de las dependencias externas. Esto ayudó a los desarrolladores de clientes en el proceso de desarrollo de características a encontrar errores en las primeras etapas.
Pero no piense que pudimos probar todo con este método. Para probar todo, es mejor usar una composición de métodos: probar con una tarjeta real en el producto, probar en el sandbox, el método de simulacros y objetos falsos, pruebas de unidad e integración. Recuerde el equilibrio de la pirámide de prueba y no intente resolver todos los problemas con un método. Esto puede llevar a una automatización triste en el sandbox, a pruebas manuales tristes con una tarjeta real de todos los casos y muchos otros errores graves en el lugar exacto donde su apariencia es más dolorosa.
Conclusión
Como resultado de nuestra investigación, obtuvimos un método económico, rápido y estable de probar no solo los servicios pagos en iOS, sino también cualquier componente que esté integrado en la aplicación como un "recuadro negro". Ahora en Badoo estamos implementando este método para probar en proveedores pagos de Android (Global Charge, Boku, Centili) que tienen entornos limitados inestables o cualquier otra restricción. También utilizamos el método moki para probar publicidad, transmisión y geolocalización.
Vale la pena decir que el proceso de introducción de un nuevo método no fue rápido. Tuve que negociar con cuatro equipos: iOS QA, iOS Dev, Billing QA, Billing Dev. No todos querían cambiar a un nuevo método, temiendo riesgos. A veces era un seguimiento dogmático: durante muchos años probamos en el sandbox, y la fuerza principal que podía destruir el dogma era el deseo de los probadores de facturación y la plataforma iOS para cambiar la situación y deshacerse del tormento. Más tarde, los desarrolladores se dieron cuenta de las ventajas de este método como diagnósticos precisos (no pudimos encontrar errores en el sandbox, sino errores de nuestro cliente o servidor), flexibilidad en la configuración del componente (pudimos probar fácilmente los casos negativos en el nivel de integración) y, por supuesto, la respuesta fue 30 minutos en una rama con código desarrollado.
Muchas gracias a todos los que han leído hasta el final. Muchas gracias a todos los que ayudaron y participaron en este proyecto. Un agradecimiento especial a estas personas:
- Peter Kolpashchikov es un desarrollador de iOS que ayudó a hacer moki en el lado del cliente y desarrolló un concepto PPP;
- Vladimir Solodov - Facturación de control de calidad, que ayudó con la API de control de calidad para generar cheques falsos y pagar desde el servidor de facturación;
- Maxim Filatov y Vasily Stepanov - Equipo de desarrollo de facturación, que ayudaron con el código del servidor de facturación;
- iOS Dev Team: desarrolladores que pudieron refactorizar nuestros pagos en un nuevo concepto, haciendo posible el uso de mokas;
- iOS QA Team es un equipo de prueba increíble que escribió un montón de autotests;
- Equipo de control de calidad de facturación: evaluadores que ayudaron a investigar problemas.