Automatisation des tests de services payants sur iOS

Pour ceux qui sont intéressés par le sujet de l'automatisation sur iOS, j'ai deux nouvelles - bonnes et mauvaises. Bon: dans l'application iOS pour les services payants, un seul point d'intégration est utilisé - les achats intégrés (achats intégrés dans l'application ). Mauvais: Apple ne fournit aucun outil pour automatiser les tests d'achat.

Dans cet article, je suggère que vous et moi recherchions une méthode d'automatisation universelle au-delà du bien et du mal d'Apple. L'article sera utile à toute personne qui intègre des services tiers qui sont une boîte noire dans leurs applications: publicité, streaming, gestion de l'emplacement, etc. Habituellement, de telles intégrations sont très difficiles à tester, car il n'y a aucun moyen de configurer de manière flexible un service tiers pour tester l'application.



Je m'appelle Victor Koronevich, je suis ingénieur principal en automatisation des tests chez Badoo. Engagé dans l'automatisation mobile depuis plus de dix ans. Avec mon collègue Vladimir Solodov, nous avons fait ce rapport lors de la conférence de Heisenbug. Il m'a également aidé à préparer ce texte.

Dans l' article précédent , nous avons décrit les méthodes utilisées par Badoo pour tester l'intégration avec les fournisseurs de paiement, dont nous avons plus de 70. Dans cet article, nous parlerons davantage de la façon dont nous avons réussi à réaliser une automatisation stable et peu coûteuse des tests de services payants dans une application iOS.

Commençons par une description générale de nos recherches:

  1. Définition du problème
  2. Énoncé du problème
  3. Solution n ° 1. Apple Sandbox
  4. Décision numéro 2. Fonction Mock, méthode et utilisation d'un faux objet
  5. Évaluation de la décision: principaux risques
  6. Résultat
  7. Conclusion

Définition du problème


L'automatisation doit être effectuée en cas de besoin naturel. Quand ce moment est-il venu avec nous?

Il y a beaucoup de fonctionnalités gratuites dans l'application Badoo, mais celles payantes offrent à l'utilisateur plus d'options. Ils les obtiennent de deux manières: pour des prêts - la monnaie interne de Badoo - ou en achetant un abonnement premium. Pour un certain nombre de crédits, vous pouvez augmenter votre profil dans les résultats de recherche en premier lieu, faire un cadeau à un autre utilisateur, etc. L'abonnement premium est valable pour une certaine période de temps et vous offre plusieurs options à la fois: activer le mode d'invisibilité, voir les personnes qui ont sympathisé avec vous, annuler le résultat de votre vote et d'autres.

Ces fonctionnalités sont apparues progressivement dans Badoo. Et il y a quelques années, nous avons testé les services payants dans les applications iOS uniquement manuellement. Mais à mesure que les fonctionnalités et les nouveaux écrans apparaissent, les tests manuels ont pris de plus en plus de temps. Les exigences pour les changements dans l'application provenaient de différents côtés: des développeurs côté client, des développeurs côté serveur et même du fournisseur Apple lui-même. Pour un testeur, une itération de test a commencé à prendre environ huit heures. Il est devenu impossible d'obtenir un feedback rapide pour un développeur sur sa branche en 30 minutes, ce qui pourrait finalement affecter négativement la compétitivité du produit.

Nous voulions obtenir les résultats des tests le plus rapidement possible. Et ils ont rencontré un problème: comment organiser les tests de régression des services payants dans nos applications iOS à moindre coût afin d'obtenir des résultats rapides et stables?

Énoncé du problème


Ainsi, en tenant compte des spécificités de notre processus de livraison du produit final et de la taille de l'équipe, nous souhaitons:

  • Testez tous les achats dans l'application client (paiements et abonnements uniques);

  • répéter les tests 10 à 20 fois par jour;
  • Obtenez les résultats des tests ~ 150 scripts de test en moins d'une demi-heure;
  • se débarrasser du bruit;
  • être en mesure d'exécuter des tests sur une branche spécifique du code développeur, quels que soient les résultats des autres exécutions.

Maintenant que nous avons formulé la tâche, il est temps de commencer le voyage dans le monde merveilleux des ingénieurs et de leurs solutions.

Solution n ° 1. Apple Sandbox


Tout d'abord, nous avons commencé à rechercher des informations sur l'organisation du test automatique des services payants dans la documentation Apple. Et ils n’ont rien trouvé. Le support d'automatisation semble très maigre. Si quelque chose apparaît, la configuration de l'automatisation avec les outils proposés est difficile (rappelons au moins UIAutomation , ainsi que le moment où le premier utilitaire xcrun simctl pour iOS Simulator est apparu) et vous devez rechercher des solutions d'ingénierie, y compris dans le segment open-source.

Dans la documentation Apple pour tester les services payants, vous ne pouvez trouver que Apple Sandbox . Il n'était pas clair comment lier ce bac à sable à l'automatisation, mais nous avons décidé de rechercher sérieusement cette solution. Le fait que le bac à sable Android soit stable nous a donné confiance, et à ce moment-là, nous avions déjà réussi les tests sur Android. Peut-être que le bac à sable d'Apple sera tout aussi bon?

Mais lorsque nous avons implémenté les autotests à l'aide de ce bac à sable, nous avons bu en entier. Passons rapidement en revue les principaux problèmes.

1. Le pool d'utilisateurs de test


La principale limitation de l'automatisation était les caractéristiques du contenu dans le pool d'utilisateurs de test, ce qui devrait garantir l'indépendance du lancement des autotests.

Pour exécuter un seul achat automatique d'un abonnement, nous avons besoin de:

  1. prendre un nouvel utilisateur pour autorisation dans le bac à sable;
  2. changer sur le simulateur l'identifiant Apple lié actuel;
  3. Connectez-vous à Badoo avec Badoo
  4. accéder à l'écran d'achat de l'abonnement et sélectionner un produit;
  5. Confirmez l'achat et connectez-vous via Apple ID;
  6. assurez-vous que l'achat a réussi;
  7. envoyer l'utilisateur Badoo pour le nettoyage;
  8. effacer l'utilisateur du bac à sable des abonnements.

Si vous essayez d'utiliser immédiatement le même utilisateur lors du prochain test, il sera impossible d'acheter un deuxième abonnement. Vous devez attendre que le premier abonnement "tourne mal", ou vous désinscrire dans les paramètres. Comme nous l'avons dit dans le premier article , le bac à sable a une période de validité d'abonnement spécifique. Si vous achetez un abonnement "pour un mois", vous devez attendre cinq minutes pour le fermer automatiquement. Le processus de désinscription lui-même n'est pas non plus rapide.

Par conséquent, pour une nouvelle exécution du même test, nous devrons soit attendre la fin de l'abonnement, soit prendre un autre utilisateur «propre». Si nous voulons exécuter deux tests simultanément indépendamment l'un de l'autre, nous devons avoir au moins deux utilisateurs de bac à sable dans le pool. Ainsi, pour exécuter 100 auto-tests en parallèle dans 100 threads, nous avons besoin de 100 utilisateurs différents.

Et maintenant, imaginons que nous effectuons un autotest sur deux agents, chacun pouvant les exécuter sur 100 threads. Dans ce cas, nous avons besoin d'au moins 200 utilisateurs!

2. Notifications «incorrectes»


Eh bien, que diable ne plaisante pas! Nous avons organisé un pool d'utilisateurs et commencé à observer le déroulement des tests. Ils sont tombés le long de la route, mais la majorité - pour de nouvelles raisons qui nous sont inconnues. Nous avons commencé à comprendre et à réaliser que lors de l'autorisation, de la confirmation d'un achat et du travail en tant qu'utilisateur dans le bac à sable, l'App Store envoie des alertes: par exemple, demande un nouveau nom d'utilisateur et un nouveau mot de passe, confirme l'autorisation en cliquant sur le bouton «OK», donne des informations sur une erreur interne avec le bouton «OK» . Parfois, ils apparaissent, parfois non. Et s'ils apparaissent, alors toujours dans un ordre différent.



Comment est-il possible qu'une erreur suspecte soit simplement ignorée dans un autotest? Et si une véritable erreur survient, que dois-je faire? Cette zone est automatiquement devenue une «zone aveugle» pour nous, et nous avons dû écrire des gestionnaires spéciaux pour toutes les alertes possibles qui pourraient arriver de l'App Store.

Tout cela a ralenti les tests:

  • les alertes pourraient arriver à différentes étapes du scénario de test, détruisant l'idée principale du test - Scénario de test prévisible; nous avons dû ajouter un gestionnaire d'erreurs qui prévoyait l'apparition d'une série d'alertes ignorées connues;
  • parfois de nouvelles variations d'alertes sont arrivées ou d'autres erreurs se sont produites, nous avons donc dû recommencer les tests tombés; cela a augmenté la durée d'exécution de tous les tests.

3. Y a-t-il eu un test?


Ainsi, les utilisateurs du pool sont bloqués, puis effacés pendant n minutes. Nous exécutons des tests dans 120 threads, et il y a déjà pas mal d'utilisateurs dans le pool, mais ce n'est pas suffisant. Nous avons créé notre système de gestion des utilisateurs, créé un gestionnaire d'alertes, puis l'informatique s'est produite. Le bac à sable est devenu indisponible pendant quelques jours pour tout utilisateur de test.

Personne ne s'y attendait. Et ce fut la dernière goutte dans le calice de notre patience, qui a finalement tué l'amour du bac à sable Apple et nous a fait embarquer sur le chemin de l'autre côté du bien et du mal. Nous avons réalisé que nous n'avions pas besoin d'une telle automatisation et que nous ne voulions plus souffrir de cette décision dangereuse.

Décision numéro 2. Fonction Mock, méthode et utilisation d'un faux objet


Nous avons donc bu des problèmes d'automatisation dans le bac à sable d'Apple. Mais ne pensez pas que dans le monde mobile, tout est complètement mauvais. Sur Android, le bac à sable est beaucoup plus stable - vous pouvez y exécuter des autotests.

Essayons de trouver une autre solution pour iOS. Mais comment regarder? Où chercher? Regardons l'histoire des tests et du développement logiciel: qu'est-il arrivé au monde fou d'Apple? Que disent les gens qui ont écrit un tas de livres et qui ont acquis une autorité dans le monde de l'automatisation et du développement de logiciels?

Je me suis immédiatement souvenu du travail «xUnit Test Patterns: Refactoring Test Code», écrit par Gerard Mesaroche ( revue par Martin Fowler), - à mon avis, l'un des meilleurs livres pour tout testeur qui connaît au moins un langage de programmation de haut niveau et veut faire de l'automatisation . Quelques chapitres de ce livre consacrés à tester SUT indépendamment des autres composants de l'application, qui sont notre «boîte noire», peuvent nous aider.

1. Introduction au moka et aux faux


Il convient de noter que dans le monde des tests automatiques, il n'y a pas de frontière généralement acceptée entre les concepts de test en double, test de talon, test d'espionnage, objet simulé, faux objet, objet factice. Vous devez toujours considérer la terminologie de l'auteur. Nous n'avons besoin que de deux concepts du grand monde des doubles de test: une fonction factice et un faux objet. Qu'est ce que c'est Et pourquoi en avons-nous besoin? Nous donnons une brève définition de ces concepts afin de ne pas avoir de désaccords.

Supposons que nous ayons une application et un composant intégrés, qui est pour nous une «boîte noire». À l'intérieur de l'application, nous pouvons appeler des fonctions en accédant à ce composant et obtenir les résultats de ces fonctions. En fonction du résultat, notre application réagit de manière spécifique. Parfois, le résultat de l'exécution de la fonction peut être une entité entière avec un tas de champs qui reflètent les données réelles de l'utilisateur.

Substitution d'une fonction à toute autre qui retourne le résultat souhaité, appelons la fonction simulée ou simplement simulée. Ces fonctions peuvent avoir la même signature, mais ce sont deux fonctions différentes.

Et la substitution d'une entité obtenue à la suite de la fonction pour une fausse entité (contenant les données nécessaires dans les champs, et parfois même des données corrompues) sera appelée la mise en œuvre d'un faux objet. Vous pouvez en savoir plus à ce sujet dans le livre que j'ai mentionné ci-dessus, ou dans tout autre recueil pour les tests et le développement de logiciels.

Pour terminer, soulignons certaines fonctionnalités de l'utilisation de fonctions simulées et de faux objets:

  1. Pour obtenir les fonctions humides, vous devez accéder au code source et savoir comment l'application fonctionne avec le composant de l'intérieur au niveau du développeur.
  2. Pour implémenter un faux objet, vous devez connaître la structure de l'objet réel.
  3. L'utilisation de la fonction de simulation permet une configuration flexible de l'application avec le composant.
  4. L'utilisation d'un faux objet vous permet de doter une entité de n'importe quelle propriété.

La méthode moki et faux objet est idéale pour isoler le fonctionnement d'un composant dans une application. Voyons comment appliquer cette méthode pour résoudre notre problème, où l'App Store sera le composant. En raison des particularités de l'utilisation de cette méthode, nous devons d'abord nous pencher sur l'étude de la nature du travail de notre application avec le composant, puis sur la mise en œuvre technique pour fabriquer des mokeys spécifiques et de faux objets.

2. Comment se déroule un achat réel


Avant de commencer à décrire l'interaction de toutes les parties du système, mettons en évidence les principaux acteurs:

  • utilisateur de l'application - tout acteur qui effectue des actions avec l'application, il peut s'agir d'une personne ou d'un script exécutant les instructions nécessaires;
  • application (dans notre cas, nous utilisons l'application Badoo iOS installée dans le simulateur iOS);
  • serveur - un acteur qui traite les demandes de l'application et renvoie des réponses ou des notifications asynchrones sans demande client (dans ce cas, nous entendons un serveur Badoo abstrait pour simplifier la structure);
  • L'App Store est un acteur qui est une «boîte noire» pour nous: nous ne savons pas comment il est organisé à l'intérieur, mais nous connaissons son interface publique pour le traitement des achats au sein de l'application ( framework StoreKit ), et sait également comment vérifier les données sur un serveur Apple.

Voyons comment se déroule l'achat. L'ensemble du processus peut être vu dans le diagramme:


Figure 1. Schéma de paiement sur l'App Store

Nous décrirons pas à pas les principales actions des acteurs.

1. Le point de départ est l'état de tous les acteurs avant d'ouvrir l'écran avec une liste de produits.

Qu'est-ce que cet écran et comment en sommes-nous arrivés là?

Supposons qu'un utilisateur trouve une personne intéressante, ouvre son profil, rédige un message et souhaite envoyer un cadeau. L'envoi d'un cadeau est un service payant. L'utilisateur peut faire défiler le profil jusqu'à la section d'envoi de cadeaux ou sélectionner immédiatement un cadeau dans le chat.

Si l'utilisateur sélectionne un cadeau et n'a pas d'argent sur le compte, il verra une liste de différents packages de prêts (Assistant de paiement) à acheter. Le point de départ de notre exemple est une liste de cadeaux. Dans le schéma, on peut considérer un tel point n'importe quel écran avant d'afficher la liste des produits pour l'achat de prêts ou d'abonnement.

2. Ouverture d'une liste de produits.

Nous sommes au point de départ, par exemple, sur la liste de cadeaux. L'utilisateur sélectionne l'un des cadeaux dans l'application. L'application fait une demande à notre serveur pour obtenir une liste des packages de prêt ID produit possibles (100, 550, 2000, 5000). Le serveur renvoie cette liste à l'application.

Ensuite, l'application envoie la liste des ID produit reçue pour vérification à l'acteur de l'App Store (framework iOS du système StoreKit qui va au serveur Apple). Il renvoie une liste de produits éprouvés - et, par conséquent, l'application montre à l'utilisateur la liste finale des packages de prêt avec des icônes et des prix.

3. Sélection des produits et génération de reçus.

L'utilisateur sélectionne un produit payant. L'App Store nécessite une preuve d'achat et une autorisation via l'identifiant Apple. Une fois l'autorisation utilisateur réussie, le contrôle est transféré à l'application. L'application attend la génération d'un reçu dans son propre package. L'utilisateur à ce moment voit le soleil, qui verrouille l'écran. Ce reçu a été généré peut être compris à l'aide de la méthode appStoreReceiptURL de la classe Bundle . Une fois le chèque généré par l'App Store, l'application sélectionne le chèque dans son package et envoie une demande avec le chèque et les données utilisateur au serveur Badoo.

4. Vérification de la vérification sur le serveur Badoo.

Dès que le serveur Badoo reçoit le chèque et les données utilisateur, il les renvoie au côté serveur Apple pour effectuer le premier cycle de vérification. C'est l'une des recommandations d'Apple. Ensuite, dans ce premier cycle de vérification, le serveur reçoit des informations sur l'état actuel de l'abonnement.

5. Envoi de notifications push (notification push) à partir du serveur.

Le serveur Badoo traite à nouveau les informations reçues après vérification par Apple et envoie à l'application une réponse accompagnée d'une notification push.

6. Notification push dans l'application.

S'il s'agissait d'un achat de prêts, le solde de l'utilisateur dans l'application changera immédiatement et il verra le cadeau envoyé dans le chat. S'il s'agit d'un achat d'abonnement, l'utilisateur doit attendre la notification push finale que l'abonnement est activé.

3. Détermination des dépendances et boucle de test



Pour une discussion plus approfondie, nous introduisons deux autres concepts - la dépendance externe et le circuit de test.

Dépendance externe


Par dépendances externes, nous entendons toute interaction avec un composant, qui est pour nous une «boîte noire». Dans ce cas, l'App Store agit comme un tel composant sous la forme d'un cadre système iOS (StoreKit), avec lequel notre application iOS fonctionne, et d'un serveur Apple, où vont les demandes de vérification.

La gestion de ces dépendances en conditions réelles est impossible, l'application est obligée de répondre aux signaux de sortie de la boîte noire (voir Fig. 2).

Nous avons trois dépendances externes:

  1. Vérification des produits StoreKit.
  2. Réception et remplacement d'un reçu d'achat.
  3. Vérification d'un chèque sur un serveur Badoo.


Figure 2. Dépendances externes

Circuit d'essai


Circuit de test - ce sont des sections du chemin que nous allons parcourir et vérifier pendant le processus de test.


Figure 3. Boucle de test

, , .

.

4. :


PPP-, Payment Provider. App Store (StoreKit) , :

  1. prepare — , ;
  2. makePayment — , .

iOS , Mock Payment Provider. StoreKit . « »? prepare makePayment, , . , .

№1. StoreKit


prepare, . , . , .


4.

Payment Provider. . 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 } ... } 

Listing 1. Maquette du client

Chez le fournisseur de paiement simulé, nous pouvons voir la mise en œuvre de la méthode de préparation. La magie de moka s'avère être très simple: la méthode a sauté la vérification des produits du côté StoreKit, et elle renvoie simplement une liste entrante de produits. L'implémentation réelle de prepare ressemble à ceci:

 public func prepare(products: [BMProduct]) -> [BMProduct] { let validatedProducts = self.productsSource.validate(products: products) return validatedProducts } 

Listing 2. Fournisseur de paiement en magasin réel

Dépendance n ° 2. Réception et remplacement d'un reçu d'achat


La deuxième dépendance est un peu plus compliquée: nous devons d'abord supprimer l'autorisation afin de ne pas conserver le pool de comptes d'utilisateurs, puis obtenir le contrôle lui-même. Nous pouvons simplement supprimer le formulaire d'autorisation:


Figure 5. Suppression d'un formulaire d'autorisation lors d'un paiement

Ce n'est pas si simple avec un chèque. Les questions sont nombreuses:

  1. Comment obtenir à l'avance un reçu pour le bon produit?
  2. Si nous avons reçu le chèque, alors quand et comment le joindre à la demande?

Ici, l'acteur "Utilisateur" a un nouveau rôle - QA. Lorsque nous exécutons le test, nous pouvons non seulement cliquer sur les boutons de l'interface, mais également appeler les méthodes API du cadre de test (méthodes qui simulent les actions de l'utilisateur) et les services API REST (méthodes qui peuvent faire de la magie à partir du service Badoo interne). Chez Badoo, nous utilisons un outil API QA très puissant (vous pouvez trouver toutes ses capacités sur le lien: https://vimeo.com/116931200 ). C'est lui qui nous aide à tester et vérifie le bon produit côté serveur de Badoo. Le serveur Badoo est le meilleur endroit pour générer des chèques: il y a cryptage et décryptage du chèque, donc le serveur sait tout sur cette structure de données.

Après avoir reçu un faux chèque, nous pouvons le faire passer par une porte dérobée côté application. Ensuite, l'application enverra un faux chèque avec les données utilisateur à notre serveur.


Figure 6. Schéma de réception

Comment cela est-il devenu techniquement possible?

1. Pour configurer un faux chèque dans l'application, nous avons pu utiliser une porte dérobée qui a enregistré le faux chèque dans le champ MockPaymentProvider de la réception:

 #if BUILD_FOR_AUTOMATION @objc extension BadooAppDelegate { @objc func setMockPurchaseReceipt(_ receipt: String?) { PaymentProvidersFactory.useMockPaymentProviderForITunesPayments = true MockPaymentProvider.receipt = receipt } ... } #endif 

Listing 3. Fausse porte dérobée

2. L'application a pu prendre notre chèque grâce à MockPaymentProvider, dans lequel nous avons utilisé la maquette makePayment et le chèque enregistré dans 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) } } } 

Listing 4. Appel d'un moka de traitement des achats avec un faux chèque

3. Obtenir un faux chèque

Pour obtenir un faux contrôle, nous avons utilisé la méthode sur le serveur (voir Listing 5). Il prend un tableau par défaut avec des données pour générer des données de contrôle et y ajoute les données nécessaires pour un produit particulier.

 $new_receipt_model = array_replace_recursive( //       $this->getDefaultModel(), //       //,      $this->enrichModelUsingSubscription($nr), //        $this->enrichModelUsingInput($input) ); //  $new_receipt = $this->signReceipt( json_encode($new_receipt_model, true), $new_receipt_model ); 

Listing 5. Partie serveur de la génération de chèques

Pour répéter la structure d'un chèque réel, le chèque personnalisé envoyé par l'application doit être chiffré à l'aide d'un certificat. Nous utilisons notre certificat de travail au lieu du certificat Apple.

 function signReceipt($receipt, $response)  { //     base64 $receipt = 'Subject: ' . base64_encode(json_encode($response)) . PHP_EOL . PHP_EOL . $receipt; file_put_contents($receipt_file, $receipt); ... //    $sign_result = openssl_pkcs7_sign( $receipt_file, $signed_receipt_file, 'file://'.$path_cert, 'file://'.$path_key, [], PKCS7_BINARY); ... //  $signed_content_with_headers = file_get_contents($signed_receipt_file); list($headers, $signed_content) = explode(PHP_EOL . PHP_EOL, $signed_content_with_headers); //  return str_replace(["\r\n", "\r", "\n"], '', $signed_content); } 

Listing 6. Méthode de signature d'un chèque avec un certificat

4. En conséquence, dans le test, nous obtenons:

 (/       "((\d+) |  (\d+) ?/) do |service_type| #    service_details = parse_options(service_type) #  QA API (  Badoo) receipt = QaApi::Billing.order_get_app_store_receipt(service_details) #   Backdoors.set_fake_receipt(receipt) end 

Listing 7. Étape de test de Gherkin pour le framework Cucumber

Dépendance n ° 3. Vérification d'un chèque sur un serveur Badoo


Pour supprimer la troisième dépendance, vous devez vous débarrasser de la vérification de vérification sur le serveur. Il est important de se rappeler que la vérification se fait en deux étapes. À la première étape, le chèque est authentifié sur la base des signatures et des certificats. Le deuxième - le chèque est envoyé à l'App Store. En cas de validation réussie à ce stade, nous recevrons un chèque décrypté qui pourra être traité.


Figure 7. Suppression de la vérification du serveur

Tout d'abord, le serveur effectue la vérification initiale de la vérification dans la méthode verifyReceiptByCert de la classe parente. Cela vérifie la signature avec le certificat App Store. Dans le cas d'un faux contrôle, cette vérification échouera car elle est signée par notre certificat, et nous appellerons la méthode de vérification avec le certificat local verifyReceiptByLocalCert. Dans cette méthode, nous essaierons de déchiffrer le chèque à l'aide d'un certificat local, et en cas de succès, nous placerons le résultat du déchiffrement dans le champ interne local_receipt de la classe enfant (méthode 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; } 

Listing 8. Vérification initiale

Lors de la vérification secondaire (verifyReceipt), nous obtenons la valeur du champ local_receipt de la classe enfant getLocallyVerifiedReceipt. S'il n'est pas vide, nous utilisons sa valeur à la suite d'une vérification.

Si le champ est vide, alors nous appelons la vérification secondaire à partir de la classe parent ( parent :: verifyReceipt). Là, nous faisons une demande à l'App Store pour vérification de son côté. Le résultat de la vérification dans les deux cas est un chèque décrypté.

 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; } 

Listing 9. Vérification secondaire

5. Test vidéo: acheter des prêts et des abonnements


Numéro de test 1. Achat d'abonnement


Quand
Je me connecte à l'application en tant que nouvel utilisateur avec une photo
Et
Je génère un nouveau chèque de facturation d'abonnement d'un mois
Et
Je vais à mon profil
Alors
Je m'assure que l'abonnement est désactivé
Quand
J'ouvre la liste des produits
Et
J'achète un abonnement d'un mois
Alors
Je vérifie la notification d'achat réussie
Et
Je m'assure que l'abonnement est activé

Vidéo de test:


Numéro de test 2. Acheter des prêts et envoyer un cadeau


Quand
Je me connecte à l'application en tant que nouvel utilisateur avec une photo
Et
J'ajoute dix crédits à mon profil
Et
Je génère une nouvelle vérification de crédit pour 550 crédits
Et
Je crée un nouvel utilisateur Leela
Et
Leela a voté oui pour moi
Et
Je vais aux gens à proximité et j'ouvre un profil Leela
Et
Je vote "Oui" pour Leela
Alors
Je vérifie la page du match
Quand
Je choisis d'envoyer un cadeau régulier
Alors
Je vérifie l'écran de paiement avec une liste de colis
Quand
Je choisis d'acheter 550 crédits
Alors
Je vérifie la notification d'achat réussie
Et
Je m'assure que Leela a reçu un cadeau de chat


Vidéo de test:



Évaluation de la décision: principaux risques


La suppression des dépendances externes comporte certains risques.

1. Configuration incorrecte.

Étant donné que la vérification n'est pas de notre côté, nous pouvons mal configurer nos produits du côté Apple. Pour nous protéger de l'erreur, nous avons écrit un test unitaire côté serveur distinct, qui vérifie que tous les produits que nous démarrons côté Apple correspondent aux produits que nous avons dans notre configuration.

2. Cas limites.

Par exemple, lorsque le paiement est entièrement effectué, l'utilisateur reçoit une notification indiquant qu'il a effectué, mais notre application ne trouve pas le chèque qui doit être falsifié à la suite de ce paiement. Le risque réside dans le fait que nous attachons nous-mêmes le chèque à l'aide d'une porte dérobée, et nous ne pouvons naturellement pas suivre un tel cas. Afin de compenser en quelque sorte ce risque, nous effectuons des contrôles de bout en bout en utilisant le bac à sable ou un véritable paiement après la libération.

3. Faux déloyaux ou fraude.

Après avoir lu cet article, vous pourriez penser qu'étant donné que Badoo utilise de faux chèques, vous pouvez nous attacher quelque chose de faux et utiliser le service gratuitement. Pour que ce risque ne se matérialise pas, nous signons tout avec notre propre certificat et limitons l'utilisation de moks et de faux contrôles à des tests fonctionnels qui ne sont exécutés que dans notre environnement de développement.

4. Modifiez le format du chèque.

C'est le risque le plus sérieux. Changer le format d'un chèque est possible quand Apple change quelque chose sans nous en avertir. Nous avons eu un tel cas: lors du passage à iOS 11, le format du chèque a complètement changé. Nous avons généré un faux chèque sur notre serveur et l'avons utilisé dans le test. Tout était parfait avec nous: tous les champs sont en place, tout est merveilleux, tout est en cours de traitement. Mais lorsque nous sommes passés au système réel, rien n'a fonctionné. Les champs importants sur le chèque ont tout simplement cessé d'exister.

Comment compenser ce risque? Premièrement, nous n'excluons pas la possibilité de tester de bout en bout le bac à sable avant la sortie et le paiement réel - après la sortie. Nous sommes maintenant dans la phase active d'un projet de vérification des notifications, lorsque nous essayons de classer tous les chèques que nous recevons de la production selon que nous comprenons ce que c'est ou ne comprenons pas. Si la réponse est non, alors nous commençons à tout traiter manuellement, voir ce qui a changé, ce qui ne va pas, ce qui doit être changé dans notre système.
Risque
Raison
Comment compenser
mauvaise configuration
supprimer le chèque
test unitaire sur le serveur
cas de bord
(chèque non remis)
utiliser une porte dérobée
Chèques E2E (bac à sable et paiement réel)
fraude frauduleuse, fraude
notification et génération de chèques sur le serveur
propre certificat
changer le format du chèque
notification et génération de chèques sur le serveur
vérification des notifications réelles et vérification de la prod (nouveau projet),
Chèques E2E (bac à sable et paiement réel)

Résultat



Considérez les principaux avantages que nous avons pu obtenir grâce à l'application de la méthode moki et du faux objet.

Automatisation peu coûteuse, rapide et stable des services payants sur iOS


En collaboration avec l'équipe de tests manuels iOS (merci spécial à Colin Chan), nous avons pu écrire plus de 150 auto-tests pour les paiements. Il s'agit d'une couverture assez importante pour un domaine de l'application.

Grâce à la parallélisation, nous pouvons obtenir le résultat en seulement 15 à 20 minutes sur n'importe quelle branche du développeur client iOS ou du serveur de facturation. Avant l'automatisation, le test manuel de cette zone par une seule personne prenait huit heures.

Nous pouvons également tester la grande majorité des cas de test en configurant le fournisseur de paiement simulé via le moki de la manière dont nous avons besoin. Avec l'aide de mooks, nous avons appris comment désactiver la vérification du produit et simuler les cas lorsque la vérification est partiellement effectuée. Ainsi, nous avons ouvert des dossiers que nous ne pouvions pas tester en principe auparavant.

Régression fonctionnelle dans le développement de nouvelles fonctionnalités


L'automatisation a très bien fonctionné dans les cas où le développeur en train de travailler sur une nouvelle fonctionnalité affectait l'ancienne fonctionnalité. Nous avons eu un exemple lorsqu'un développeur a fait une fonctionnalité complexe avec la mise en cache et a exécuté nos tests automatiques. Certains d'entre eux se sont trompés. Il l'a vu et l'a réparé. Puis il a recommencé les autotests - et encore, quelque chose est tombé. En conséquence, il a fait une série d'itérations jusqu'au moment où tout a commencé à fonctionner normalement du côté de l'application.

Régression fonctionnelle dans la refactorisation des paiements


L'automatisation la plus réussie et la plus efficace possible se produit peut-être dans le domaine du refactoring de code. Dans ce cas, seule l'implémentation interne change - il n'est pas nécessaire de modifier le code d'autotest. L'interface utilisateur ne change en aucune façon et les autotests peuvent être pilotés efficacement.

Test des fonctionnalités expérimentales d'Apple: période de grâce


Un système similaire est complètement interchangeable lorsque vous testez de nouvelles intégrations qui ne sont pas encore implémentées dans le bac à sable. Il en était de même de la période de grâce. Cette fonctionnalité n'est pas dans le bac à sable. Le délai de grâce sur Apple n'est pas encore accessible à tous. Il s'agit d'un projet pilote que Badoo met en œuvre avec Apple. Afin de vérifier avec un délai de grâce, nous devions ajouter ici un tel morceau de code 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 }] 

Listing 10. Délai de grâce pour un abonnement

Nous l'avons fait très facilement en quelques secondes. Dans notre système, nous avons pu tester notre réaction à une nouvelle fonctionnalité. Maintenant, nous exécutons cette fonctionnalité sur le prod.

Tests de qualité des produits dans les méthodes de composition


À la suite de nos recherches, nous avons pu décrire une méthode qui élimine le bruit des dépendances externes. Cela a aidé les développeurs clients dans le processus de développement de fonctionnalités à trouver des bogues au début.

Mais ne pensez pas que nous ayons pu tout tester avec cette méthode. Pour tout tester, il est préférable d'utiliser une composition de méthodes: tester avec une vraie carte sur le prod, tester dans le bac à sable, la méthode des mokes et des faux objets, les tests unitaires et d'intégration. N'oubliez pas l'équilibre de la pyramide des tests et n'essayez pas de résoudre tous les problèmes avec une seule méthode. Cela peut conduire à une triste automatisation dans le bac à sable, à de tristes tests manuels avec une vraie carte de tous les cas, et à de nombreuses autres erreurs graves à l'endroit exact où leur apparence est la plus douloureuse.

Conclusion


À la suite de nos recherches, nous avons obtenu une méthode peu coûteuse, rapide et stable de tester non seulement les services payants sur iOS, mais aussi tous les composants qui sont intégrés dans l'application comme une «boîte noire». Maintenant, chez Badoo, nous mettons en œuvre cette méthode pour tester les fournisseurs payants Android (Global Charge, Boku, Centili) qui ont des bacs à sable instables ou toute autre restriction. Nous utilisons également la méthode moki pour tester la publicité, le streaming et la géolocalisation.

Il convient de dire que le processus d'introduction d'une nouvelle méthode n'a pas été rapide. J'ai dû négocier avec quatre équipes: iOS QA, iOS Dev, Billing QA, Billing Dev. Tout le monde ne voulait pas passer à une nouvelle méthode, craignant les risques. Parfois, c'était une suite dogmatique: pendant de nombreuses années, nous avons testé dans le bac à sable, et la principale force qui pouvait détruire le dogme était le désir des testeurs de facturation et de la plate-forme iOS de changer la situation et de se débarrasser des tourments. Plus tard, les développeurs ont réalisé des avantages de cette méthode comme des diagnostics précis (nous n'avons pas pu trouver de bogues dans le bac à sable, mais des bogues de notre client ou serveur), une flexibilité dans la configuration du composant (nous avons pu facilement tester des cas négatifs au niveau de l'intégration) et, bien sûr, la réponse était 30 minutes sur une branche avec du code développé.

Un grand merci à tous ceux qui ont lu jusqu'à la fin. Un grand merci à tous ceux qui ont aidé et participé à ce projet. Un merci spécial à ces personnes:

  • Peter Kolpashchikov est un développeur iOS qui a aidé à faire moki côté client et a développé un concept PPP;
  • Vladimir Solodov - Billing QA, qui a aidé l'API QA à générer de faux chèques et à partir du serveur de facturation;
  • Maxim Filatov et Vasily Stepanov - Billing Dev Team, qui ont aidé avec le code du serveur de facturation;
  • iOS Dev Team - développeurs qui ont pu refactoriser nos paiements dans un nouveau concept, rendant possible l'utilisation de mokas;
  • iOS QA Team est une équipe de test impressionnante qui a écrit un tas d'autotests;
  • Équipe QA de facturation - testeurs qui ont aidé à rechercher des problèmes.

Source: https://habr.com/ru/post/fr460667/


All Articles