Für diejenigen, die sich für das Thema Automatisierung unter iOS interessieren, habe ich zwei Neuigkeiten - gut und schlecht. Gut: In der iOS-Anwendung für kostenpflichtige Dienste wird nur ein Integrationspunkt verwendet - In-App-Käufe (
in der Anwendung integrierte Käufe ). Schlecht: Apple bietet keine Tools zur Automatisierung von Testkäufen an.
In diesem Artikel schlage ich vor, dass Sie und ich nach einer universellen Automatisierungsmethode suchen, die über Apples Gut und Böse hinausgeht. Der Artikel ist für alle nützlich, die Dienste von Drittanbietern, die eine Black Box darstellen, in ihre Anwendungen integrieren: Werbung, Streaming, Standortverwaltung usw. Normalerweise sind solche Integrationen sehr schwer zu testen, da es keine Möglichkeit gibt, einen Dienst von Drittanbietern flexibel zum Testen der Anwendung zu konfigurieren.
Mein Name ist Victor Koronevich, ich bin Senior Test Automation Engineer bei Badoo. Seit mehr als zehn Jahren in der mobilen Automatisierung tätig. Zusammen mit meinem Kollegen Vladimir Solodov haben wir diesen Bericht auf der Heisenbug-Konferenz erstellt. Er half mir auch bei der Vorbereitung dieses Textes.Im vorherigen
Artikel haben wir beschrieben, mit welchen Methoden Badoo die Integration mit Zahlungsanbietern testet, von denen wir mehr als 70 haben. In diesem Artikel werden wir mehr darüber sprechen, wie wir es geschafft haben, eine stabile und kostengünstige Automatisierung des Testens von kostenpflichtigen Diensten in einer iOS-Anwendung zu erreichen.
Beginnen wir mit einer allgemeinen Beschreibung unserer Forschung:
- Problemdefinition
- Erklärung des Problems
- Lösung Nr. 1. Apple Sandbox
- Entscheidung Nr. 2. Funktions-Mock-Methode und Verwenden eines gefälschten Objekts
- Bewertung der Entscheidung: Hauptrisiken
- Ergebnis
- Fazit
Problemdefinition
Automatisierung muss durchgeführt werden, wenn ein natürlicher Bedarf entsteht. Wann kam dieser Moment mit uns?
Es gibt viele kostenlose Funktionen in der Badoo-App, aber kostenpflichtige bieten dem Benutzer mehr Optionen. Sie erhalten sie auf zwei Arten: für Kredite - die interne Währung von Badoo - oder durch den Kauf eines Premium-Abonnements. Für eine bestimmte Anzahl von Credits können Sie Ihr Profil in den Suchergebnissen auf den ersten Platz heben, einem anderen Benutzer ein Geschenk machen und vieles mehr. Das Premium-Abonnement ist für einen bestimmten Zeitraum gültig und bietet mehrere Optionen gleichzeitig: Aktivieren Sie den Unsichtbarkeitsmodus, sehen Sie Personen, die Sympathie für Sie gezeigt haben, stornieren Sie das Ergebnis Ihrer Abstimmung und andere.
Diese Funktionen wurden nach und nach in Badoo angezeigt. Und vor ein paar Jahren haben wir kostenpflichtige Dienste in iOS-Anwendungen nur manuell getestet. Da jedoch Funktionen und neue Bildschirme angezeigt werden, dauerte das manuelle Testen immer länger. Die Anforderungen für Änderungen in der Anwendung kamen von verschiedenen Seiten: von den Entwicklern der Clientseite, den Entwicklern der Serverseite und sogar vom Apple-Anbieter selbst. Für einen Tester dauerte eine Testiteration ungefähr acht Stunden. Es wurde unmöglich, innerhalb von 30 Minuten ein schnelles Feedback für einen Entwickler in seiner Branche zu erhalten, was sich letztendlich negativ auf die Wettbewerbsfähigkeit des Produkts auswirken könnte.
Wir wollten so schnell wie möglich Testergebnisse erhalten. Und sie stießen auf ein Problem: Wie können Regressionstests für kostenpflichtige Dienste in unseren iOS-Anwendungen kostengünstig organisiert werden, um schnelle und stabile Ergebnisse zu erzielen?
Erklärung des Problems
Unter Berücksichtigung der Besonderheiten unseres Lieferprozesses des Endprodukts und der Größe des Teams möchten wir:
- Testen Sie alle Einkäufe innerhalb der Kundenanwendung (einmalige Zahlungen und Abonnements).
- Wiederholen Sie die Testiterationen 10 bis 20 Mal am Tag.
- Erhalten Sie Testergebnisse ~ 150 Testskripte in weniger als einer halben Stunde;
- Lärm loswerden;
- Sie können Tests für einen bestimmten Zweig des Entwicklercodes ausführen, unabhängig von den Ergebnissen anderer Läufe.
Nachdem wir die Aufgabe formuliert haben, ist es Zeit, die Reise in die wunderbare Welt der Ingenieure und ihrer Lösungen zu beginnen.
Lösung Nr. 1. Apple Sandbox
Zunächst suchten wir in der Apple-Dokumentation nach Informationen zum Organisieren des automatischen Testens kostenpflichtiger Dienste. Und sie haben nichts gefunden. Die Automatisierungsunterstützung sieht sehr dürftig aus. Wenn etwas angezeigt wird, ist es schwierig, die Automatisierung mit den vorgeschlagenen Tools
einzurichten (erinnern wir uns zumindest an
UIAutomation sowie an die Zeit, als das erste
xcrun simctl- Dienstprogramm für iOS Simulator erschien), und Sie müssen nach technischen Lösungen suchen, auch im Open-Source-Segment.
In der Apple-Dokumentation zum Testen kostenpflichtiger Dienste finden Sie nur
Apple Sandbox . Es war nicht klar, wie diese Sandbox mit der Automatisierung verbunden werden sollte, aber wir haben uns entschlossen, diese Lösung ernsthaft zu untersuchen. Die Tatsache, dass die Android-Sandbox stabil war, gab uns Vertrauen, und zu diesem Zeitpunkt hatten wir bereits erfolgreich Tests für Android geschrieben. Vielleicht ist die Apple Sandbox genauso gut?
Aber als wir Autotests mit dieser Sandbox implementiert haben, haben wir voll getrunken. Lassen Sie uns kurz auf die Hauptprobleme eingehen.
1. Der Pool der Testbenutzer
Die Haupteinschränkung für die Automatisierung sind die Merkmale des Inhalts im Pool der Testbenutzer, die die Unabhängigkeit des Starts von Autotests gewährleisten sollen.
Um nur einen automatischen Kauf eines Abonnementkaufs durchzuführen, benötigen wir:
- Nehmen Sie einen neuen Benutzer zur Autorisierung in die Sandbox.
- Ändern Sie auf dem Simulator die aktuell verknüpfte Apple ID.
- Melden Sie sich mit Badoo bei Badoo an
- Rufen Sie den Bildschirm des Abonnementkaufs auf und wählen Sie ein Produkt aus.
- Bestätigen Sie den Kauf und melden Sie sich über die Apple ID an.
- Stellen Sie sicher, dass der Kauf erfolgreich war.
- Badoo-Benutzer zur Reinigung senden;
- Löschen Sie den Benutzer der Sandbox von Abonnements.
Wenn Sie versuchen, im nächsten Test sofort denselben Benutzer zu verwenden, ist es unmöglich, ein zweites Abonnement zu kaufen. Sie müssen warten, bis das erste Abonnement "schlecht" wird, oder sich in den Einstellungen abmelden. Wie bereits im ersten
Artikel erwähnt , hat die Sandbox eine bestimmte Gültigkeitsdauer für Abonnements. Wenn Sie ein Abonnement "für einen Monat" kaufen, müssen Sie fünf Minuten warten, um es automatisch zu schließen. Der Abmeldevorgang selbst ist ebenfalls nicht schnell.
Dementsprechend müssen wir für einen neuen Durchlauf desselben Tests entweder warten, bis das Abonnement endet, oder einen anderen „sauberen“ Benutzer nehmen. Wenn wir zwei Tests gleichzeitig unabhängig voneinander ausführen möchten, müssen mindestens zwei Sandbox-Benutzer im Pool vorhanden sein. Um 100 Autotests parallel in 100 Threads auszuführen, benötigen wir 100 verschiedene Benutzer.
Stellen wir uns nun vor, wir führen einen Selbsttest mit zwei Agenten durch, von denen jeder sie in 100 Threads ausführen kann. In diesem Fall benötigen wir mindestens 200 Benutzer!
2. "Schlechte" Benachrichtigungen
Nun, was zum Teufel scherzt nicht! Wir organisierten einen Benutzerpool und begannen zu beobachten, wie Tests ablaufen. Sie fielen die Straße entlang, aber die Mehrheit - aus neuen, uns unbekannten Gründen. Wir begannen zu verstehen und erkannten, dass der App Store beim Autorisieren, Bestätigen eines Kaufs und Arbeiten als Benutzer in der Sandbox Warnungen sendet: Er fragt beispielsweise nach einem neuen Benutzernamen und Passwort, bestätigt die Autorisierung durch Klicken auf die Schaltfläche „OK“ und gibt Informationen über einen internen Fehler mit der Schaltfläche „OK“ aus . Manchmal erscheinen sie, manchmal nicht. Und wenn sie erscheinen, dann immer in einer anderen Reihenfolge.

Wie ist es möglich, dass ein verdächtiger Fehler bei einem Autotest einfach ignoriert wird? Und wenn ein echter Fehler eintritt, was soll ich dann tun? Dieser Bereich wurde für uns automatisch zu einer „blinden Zone“, und wir mussten spezielle Handler für alle möglichen Warnungen schreiben, die aus dem App Store kommen konnten.
All dies verlangsamte die Tests:
- Warnungen können zu verschiedenen Schritten des Testszenarios führen und die Hauptidee des Tests zerstören - Vorhersagbares Testszenario; Wir mussten einen Fehlerbehandler hinzufügen, der erwartete, dass eine mögliche Reihe bekannter ignorierter Warnungen angezeigt wird.
- Manchmal kamen neue Variationen von Warnungen an oder es traten andere Fehler auf, sodass wir die gefallenen Tests neu starten mussten. Dies erhöhte die Laufzeit aller Tests.
3. Gab es einen Test?
Benutzer im Pool werden also blockiert und dann für n Minuten gelöscht. Wir führen Tests in 120 Threads durch, und es gibt bereits ziemlich viele Benutzer im Pool, aber das reicht nicht aus. Wir haben unser Benutzerverwaltungssystem erstellt, einen Alert-Handler erstellt - und dann ist die IT passiert. Die Sandbox ist für einige Testbenutzer seit einigen Tagen nicht mehr verfügbar.
Niemand hat das erwartet. Und dies war der letzte Strohhalm im Kelch unserer Geduld, der schließlich die Liebe zum Apfel-Sandkasten tötete und uns auf den Weg jenseits von Gut und Böse brachte. Wir haben erkannt, dass wir eine solche Automatisierung nicht brauchen und dass wir mit dieser gefährlichen Entscheidung nicht mehr leiden wollen.
Entscheidung Nr. 2. Funktions-Mock-Methode und Verwenden eines gefälschten Objekts
Also haben wir Probleme mit der Automatisierung in der Sandbox von Apple getrunken. Aber denken Sie nicht, dass in der mobilen Welt alles völlig schlecht ist. Unter Android ist die Sandbox viel stabiler - Sie können dort Autotests ausführen.
Versuchen wir, eine andere Lösung für iOS zu finden. Aber wie soll man aussehen? Wo soll man suchen? Schauen wir uns die Geschichte des Testens und der Softwareentwicklung an: Was ist mit der verrückten Welt von Apple passiert? Was sagen Menschen, die eine Reihe von Büchern geschrieben und Autorität in der Welt der Automatisierung und Softwareentwicklung erworben haben?
Ich erinnerte mich sofort an die Arbeit „xUnit Test Patterns: Refactoring Test Code“ von Gerard Mesaroche (
Rezension von Martin Fowler) - meiner Meinung nach eines der besten Bücher für jeden Tester, der mindestens eine Programmiersprache auf hohem Niveau kennt und Automatisierung betreiben möchte . Ein paar Kapitel dieses Buches, die sich dem isolierten Testen von SUT von anderen Komponenten der Anwendung widmen, die unsere „Black Box“ sind, können uns helfen.
1. Einführung in Mokka und Fake
Es sollte beachtet werden, dass es in der Welt des automatischen Testens keine allgemein akzeptierte Grenze zwischen den Konzepten Testdoppel, Teststub, Testspion, Scheinobjekt, gefälschtes Objekt, Dummy-Objekt gibt. Sie müssen immer die Terminologie des Autors berücksichtigen. Wir brauchen nur zwei Konzepte aus der großen Welt der Test Doubles: eine Scheinfunktion und ein gefälschtes Objekt. Was ist das? Und warum brauchen wir das? Wir geben eine kurze Definition dieser Konzepte, damit wir keine Meinungsverschiedenheiten haben.
Angenommen, wir haben eine Anwendung und eine Komponente eingebaut, die für uns eine „Black Box“ ist. Innerhalb der Anwendung können wir Funktionen aufrufen, indem wir auf diese Komponente zugreifen und die Ergebnisse dieser Funktionen abrufen. Je nach Ergebnis reagiert unsere Anwendung spezifisch. Manchmal kann das Ergebnis der Funktionsausführung eine ganze Entität mit einer Reihe von Feldern sein, die die tatsächlichen Benutzerdaten widerspiegeln.
Wenn Sie eine Funktion durch eine andere ersetzen, die das gewünschte Ergebnis zurückgibt, rufen Sie den Mock der Funktion oder einfach den Mock auf. Diese Funktionen haben möglicherweise dieselbe Signatur, es handelt sich jedoch um zwei verschiedene Funktionen.
Und das Ersetzen einer Entität, die als Ergebnis der Funktion für eine gefälschte Entität erhalten wird (die die erforderlichen Daten in den Feldern und manchmal sogar beschädigte Daten enthält), wird als Implementierung eines gefälschten Objekts bezeichnet. Sie können mehr darüber in dem oben erwähnten Buch oder in einem anderen Kompendium zum Testen und zur Softwareentwicklung lesen.
Lassen Sie uns abschließend einige Funktionen zur Verwendung von Scheinfunktionen und gefälschten Objekten hervorheben:
- Um die Funktionen nass zu machen, müssen Sie auf den Quellcode zugreifen und wissen, wie die Anwendung auf Entwicklerebene von innen mit der Komponente funktioniert.
- Um ein gefälschtes Objekt zu implementieren, müssen Sie die Struktur des realen Objekts kennen.
- Die Verwendung der Mock-Funktion ermöglicht eine flexible Konfiguration der Anwendung mit der Komponente.
- Durch die Verwendung eines gefälschten Objekts können Sie einer Entität beliebige Eigenschaften verleihen.
Die Moki- und Fake-Object-Methode ist ideal, um den Betrieb einer Komponente innerhalb einer Anwendung zu isolieren. Mal sehen, wie wir diese Methode anwenden können, um unser Problem zu lösen, wobei der App Store die Komponente sein wird. Aufgrund der Besonderheiten dieser Methode müssen wir uns zunächst mit der Untersuchung der Art der Arbeit unserer Anwendung mit der Komponente befassen und dann mit der technischen Implementierung, um bestimmte Mokeys und gefälschte Objekte herzustellen.
2. Wie kommt es zu einem echten Kauf?
Bevor wir beginnen, die Interaktion aller Teile des Systems zu beschreiben, wollen wir die Hauptakteure hervorheben:
- Anwendungsbenutzer - Jeder Akteur, der Aktionen mit der Anwendung ausführt, kann eine Person sein oder es kann ein Skript geben, das die erforderlichen Anweisungen ausführt.
- Anwendung (in unserem Fall verwenden wir die im iOS-Simulator installierte Badoo iOS-Anwendung);
- Server - ein Akteur, der Anforderungen von der Anwendung verarbeitet und Antworten oder asynchrone Benachrichtigungen ohne Clientanforderung zurücksendet (in diesem Fall meinen wir einen abstrakten Badoo-Server, um die Struktur zu vereinfachen);
- Der App Store ist ein Akteur, der für uns eine „Black Box“ darstellt: Wir wissen nicht, wie er im Inneren angeordnet ist, aber wir kennen die öffentliche Oberfläche für die Verarbeitung von Einkäufen innerhalb der Anwendung ( StoreKit-Framework ) und wissen auch, wie Daten auf einem Apple-Server überprüft werden.
Mal sehen, wie der Kauf erfolgt. Der gesamte Prozess ist im Diagramm dargestellt:
Abbildung 1. Zahlungsschema im App StoreWir werden Schritt für Schritt die Hauptaktionen der Akteure beschreiben.
1. Ausgangspunkt ist der Status aller Akteure, bevor der Bildschirm mit einer Produktliste geöffnet wird.
Was ist dieser Bildschirm und wie sind wir darauf gekommen?
Angenommen, ein Benutzer hat eine interessante Person gefunden, sein Profil geöffnet, eine Nachricht geschrieben und wollte ein Geschenk senden. Das Senden eines Geschenks ist eine kostenpflichtige Dienstleistung. Der Benutzer kann im Profil zu dem Abschnitt zum Senden von Geschenken scrollen oder sofort ein Geschenk aus dem Chat auswählen.
Wenn der Benutzer ein Geschenk auswählt und kein Geld auf dem Konto hat, wird eine Liste der verschiedenen Darlehenspakete (Zahlungsassistent) zum Kauf angezeigt. Der Ausgangspunkt in unserem Beispiel ist eine Liste von Geschenken. In der Abbildung können wir einen solchen Punkt auf jedem Bildschirm betrachten, bevor wir die Liste der Produkte für den Kauf von Darlehen oder Abonnements anzeigen.
2. Öffnen einer Produktliste.
Wir stehen zum Beispiel auf der Geschenkliste am Ausgangspunkt. Der Benutzer wählt eines der Geschenke in der Anwendung aus. Die Anwendung fordert unseren Server auf, eine Liste möglicher Produkt-ID-Darlehenspakete (100, 550, 2000, 5000) zu erhalten. Der Server gibt diese Liste an die Anwendung zurück.
Als Nächstes sendet die Anwendung die empfangene Produkt-ID-Liste zur Überprüfung an den App Store-Akteur (StoreKit-System-iOS-Framework, das an den Apple-Server gesendet wird). Es gibt eine Liste bewährter Produkte zurück - und als Ergebnis zeigt die Anwendung dem Benutzer die endgültige Liste der Darlehenspakete mit Symbolen und Preisen.
3. Produktauswahl und Quittungserstellung.
Der Benutzer wählt ein kostenpflichtiges Produkt aus. Der App Store erfordert einen Kaufnachweis und eine Autorisierung über die Apple ID. Nach erfolgreicher Benutzerautorisierung wird die Kontrolle auf die Anwendung übertragen. Die Anwendung wartet darauf, dass eine Quittung in ihrem eigenen Paket generiert wird. Der Benutzer sieht zu diesem Zeitpunkt die Sonne, die den Bildschirm sperrt. Diese generierte Quittung kann mit der Methode appStoreReceiptURL der
Bundle- Klasse verstanden werden. Nachdem der Scheck vom App Store generiert wurde, wählt die Anwendung den Scheck aus ihrem Paket aus und sendet eine Anfrage mit dem Scheck und den Benutzerdaten an den Badoo-Server.
4. Überprüfen Sie die Prüfung auf dem Badoo-Server.
Sobald der Badoo-Server die Prüf- und Benutzerdaten erhält, sendet er diese an die Apple-Serverseite zurück, um den ersten Überprüfungszyklus durchzuführen. Dies ist eine der Empfehlungen von Apple. In diesem ersten Überprüfungszyklus erhält der Server dann Informationen über den aktuellen Status des Abonnements.
5. Senden von Push-Benachrichtigungen (Push-Benachrichtigung) vom Server.
Der Badoo-Server verarbeitet die empfangenen Informationen nach Überprüfung durch Apple erneut und sendet der Anwendung eine Antwort zusammen mit einer Push-Benachrichtigung.
6. Push-Benachrichtigung in der Anwendung.
Wenn es sich um einen Kauf von Darlehen handelte, ändert sich sofort das Guthaben des Benutzers in der Anwendung und er sieht das gesendete Geschenk im Chat. Wenn dies ein Abonnementkauf war, sollte der Benutzer auf die endgültige Push-Benachrichtigung warten, dass das Abonnement aktiviert ist.
3. Ermittlung von Abhängigkeiten und Testschleife
Zur weiteren Diskussion stellen wir zwei weitere Konzepte vor - die externe Abhängigkeit und die Testschaltung.
Externe Abhängigkeit
Unter externen Abhängigkeiten verstehen wir jede Interaktion mit einer Komponente, die für uns eine „Black Box“ ist. In diesem Fall fungiert der App Store als solche Komponente in Form eines iOS-Systemframeworks (StoreKit), mit dem unsere iOS-Anwendung funktioniert, und eines Apple-Servers, auf dem Überprüfungsanforderungen gestellt werden.
Das Verwalten dieser Abhängigkeiten unter realen Bedingungen ist nicht möglich. Die Anwendung ist gezwungen, auf die Ausgangssignale der Black Box zu reagieren (siehe Abb. 2).
Wir haben drei externe Abhängigkeiten:
- Überprüfen von StoreKit-Produkten.
- Empfangen und Ersetzen eines Kaufbelegs.
- Überprüfen einer Prüfung auf einem Badoo-Server.
Abbildung 2. Externe AbhängigkeitenTestschaltung
Testschaltung - Dies sind Abschnitte des Pfades, den wir während des Testprozesses durchlaufen und überprüfen werden.
Abbildung 3. TestschleifeDas Ziel unserer Arbeit zur Beseitigung von Abhängigkeiten ist es, eine Testschaltung zu erstellen, die so nah wie möglich am tatsächlichen Pfad liegt und es Ihnen ermöglicht, alle externen Abhängigkeiten auszuschließen und die Kontrolle auf Ihre Seite zu übertragen.
Wir betrachten jede Abhängigkeit nacheinander.
4. Isolation von Abhängigkeiten: technische Umsetzung
In unserem Unternehmen wurde für die Implementierung von Zahlungen ein PPP-Konzept verwendet, das auf der Schnittstelle des Zahlungsanbieters basiert. Dies ist die Hauptschnittstelle für die Interaktion mit dem App Store-Akteur (StoreKit) in unserer Anwendung, die über zwei Hauptmethoden verfügt:
- vorbereiten ist die Methode, die für die Überprüfung der Produkte verantwortlich ist;
- makePayment ist eine Methode, die einen In-App-Kauf verarbeitet.
Alle Zahlungen unter iOS wurden gemäß diesem Konzept überarbeitet, wodurch wir einen einfachen und bequemen Mock Payment Provider der Klasse erhalten konnten. Dies ist die Hauptschnittstelle für die Interaktion mit einer praktischen Kopie des StoreKit-Verhaltens in unserer Anwendung. Was bedeutet "bequemes Kopieren"? Dieser Anbieter verfügt über Verspottungen der Methoden prepare und makePayment, die das tun, was wir wollen. Schauen wir uns ein Beispiel für Codeteile an, wie wir es geschafft haben, Moki zu integrieren.
Abhängigkeit Nr. 1. Überprüfen von StoreKit-Produkten
Verwenden Sie zum Überprüfen der Produktliste die Vorbereitungsfunktion, die eine Liste der überprüften Produkte zurückgibt. Wir können das Modell verwenden, in dem wir die Prüfung deaktivieren und die eingehende Liste der Produkte als vollständig verifiziert zurücksenden. Somit wird die Abhängigkeit beseitigt.
Abbildung 4. Das erste Schema zur Beseitigung von AbhängigkeitenGanz oben in der Architektur unserer Anwendung steht der Zahlungsanbieter. Es spiegelt die Schnittstelle eines möglichen Anbieters in der Anwendung wider. Der Code zum Implementieren von mok befindet sich in der Klasse 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. Mock Client CheckBeim Mock Payment Provider können wir die Implementierung der Vorbereitungsmethode sehen. Die Magie von Moka stellt sich als sehr einfach heraus: Die Methode übersprang die Überprüfung von Produkten auf der StoreKit-Seite und gibt einfach eine eingehende Liste von Produkten zurück. Die eigentliche Implementierung von prepare sieht folgendermaßen aus:
public func prepare(products: [BMProduct]) -> [BMProduct] { let validatedProducts = self.productsSource.validate(products: products) return validatedProducts }
Listing 2. Real Store ZahlungsanbieterAbhängigkeit Nr. 2. Empfangen und Ersetzen eines Kaufbelegs
Die zweite Abhängigkeit ist etwas komplizierter: Wir müssen zuerst die Autorisierung entfernen, um den Pool der Benutzerkonten nicht zu behalten, und dann irgendwie die Prüfung selbst erhalten. Wir können das Autorisierungsformular einfach löschen:
Abbildung 5. Löschen eines Autorisierungsformulars bei der ZahlungMit einem Scheck ist das nicht so einfach. Es gibt viele Fragen:
- Wie bekomme ich im Voraus eine Quittung für das richtige Produkt?
- Wenn wir den Scheck erhalten haben, wann und wie können wir ihn in die Anwendung einfügen?
Hier hat der Schauspieler "User" eine neue Rolle - QA. Wenn wir den Test ausführen, können wir nicht nur auf die Schaltflächen auf der Benutzeroberfläche klicken, sondern auch die API-Methoden des Testframeworks (Methoden, die Benutzeraktionen simulieren) und die REST-API-Services (Methoden, die vom internen Badoo-Service aus Magie ausführen können) aufrufen. Wir bei Badoo verwenden ein sehr leistungsfähiges QA-API-Tool (alle Funktionen finden Sie unter dem Link:
https://vimeo.com/116931200 ). Er ist es, der uns beim Testen hilft und auf der Serverseite von Badoo nach dem richtigen Produkt sucht. Der Badoo-Server ist der beste Ort, um Schecks zu generieren: Der Scheck wird verschlüsselt und entschlüsselt, sodass der Server alles über diese Datenstruktur weiß.
Nachdem wir einen gefälschten Scheck erhalten haben, können wir ihn durch eine
Hintertür auf der Anwendungsseite stellen. Als nächstes sendet die Anwendung einen gefälschten Scheck zusammen mit Benutzerdaten an unseren Server.
Abbildung 6. Schema für den EmpfangWie wurde dies technisch möglich?
1. Um einen gefälschten Scheck in der Anwendung einzurichten, konnten wir eine Hintertür verwenden, die den gefälschten Scheck im Feld MockPaymentProvider der Quittung speicherte:
#if BUILD_FOR_AUTOMATION @objc extension BadooAppDelegate { @objc func setMockPurchaseReceipt(_ receipt: String?) { PaymentProvidersFactory.useMockPaymentProviderForITunesPayments = true MockPaymentProvider.receipt = receipt } ... } #endif
Listing 3. Fake Check Backdoor2. Die Anwendung konnte unseren Scheck dank MockPaymentProvider entgegennehmen, in dem wir das makePayment-Modell und den gespeicherten Scheck in MockPaymentProvider.receipt verwendet haben:
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. Aufruf eines Kaufabwicklungs-Moka mit einem gefälschten Scheck3. Einen gefälschten Scheck bekommen
Um eine gefälschte Prüfung zu erhalten, haben wir die Methode auf dem Server verwendet (siehe Listing 5). Es wird ein Standardarray mit Daten zum Generieren von Prüfdaten verwendet und die für ein bestimmtes Produkt erforderlichen Daten hinzugefügt.
$new_receipt_model = array_replace_recursive(
Listing 5. Serverteil der ScheckgenerierungUm die Struktur einer echten Prüfung zu wiederholen, muss die von der Anwendung gesendete benutzerdefinierte Prüfung mit einem Zertifikat verschlüsselt werden. Wir verwenden unser Arbeitszertifikat anstelle des Apple-Zertifikats.
function signReceipt($receipt, $response) {
Listing 6. Methode zum Signieren eines Schecks mit einem Zertifikat4. Als Ergebnis erhalten wir im Test:
(/ "((\d+) | (\d+) ?/) do |service_type|
Listing 7. Gurken- Testschritt für das Gurken- FrameworkAbhängigkeit Nr. 3. Überprüfen einer Prüfung auf einem Badoo-Server
Um die dritte Abhängigkeit zu entfernen, müssen Sie die Überprüfungsüberprüfung auf dem Server entfernen. Es ist wichtig zu bedenken, dass die Überprüfung in zwei Schritten erfolgt. In der ersten Phase wird die Prüfung anhand von Signaturen und Zertifikaten authentifiziert. Im zweiten Fall wird der Scheck an den App Store gesendet. Im Falle einer erfolgreichen Validierung zu diesem Zeitpunkt erhalten wir einen entschlüsselten Scheck, der verarbeitet werden kann.
Abbildung 7. Entfernen der ServerüberprüfungZunächst führt der Server die erste Überprüfung der Prüfung in der verifyReceiptByCert-Methode der übergeordneten Klasse durch. Dadurch wird die Signatur mit dem App Store-Zertifikat überprüft. Im Falle einer gefälschten Prüfung schlägt diese Überprüfung fehl, da sie von unserem Zertifikat signiert ist, und wir rufen die Methode zur Überprüfung mit dem lokalen Zertifikat verifyReceiptByLocalCert auf. Bei dieser Methode versuchen wir, die Prüfung mit einem lokalen Zertifikat zu entschlüsseln. Wenn dies erfolgreich ist, platzieren wir das Entschlüsselungsergebnis im internen Feld local_receipt der untergeordneten Klasse (addLocallyVerifiedReceipt-Methode).
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. ErstprüfungWährend der sekundären Überprüfung (verifyReceipt) erhalten wir den Wert des Felds local_receipt der untergeordneten Klasse getLocallyVerifiedReceipt. Wenn es nicht leer ist, verwenden wir seinen Wert als Ergebnis der Überprüfung.
Wenn das Feld leer ist, rufen wir die sekundäre Überprüfung von der übergeordneten Klasse (
parent :: verifyReceipt) auf. Dort stellen wir eine Anfrage an den App Store zur Überprüfung auf seiner Seite. Das Überprüfungsergebnis ist in beiden Fällen eine entschlüsselte Prüfung.
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. Sekundäre Überprüfung5. Videotestlauf: Kredite und Abonnements kaufen
Test Nummer 1. Abonnementkauf
Testlauf Video:

Test Nummer 2. Kredite kaufen und ein Geschenk schicken
Testlauf Video:

Bewertung der Entscheidung: Hauptrisiken
Das Entfernen externer Abhängigkeiten birgt bestimmte Risiken.
1. Falsche Konfiguration.
Da die Überprüfung nicht auf unserer Seite ist, können wir unsere Produkte auf der Apple-Seite falsch konfigurieren. Zum Schutz vor Fehlern haben wir einen separaten serverseitigen Komponententest geschrieben, der überprüft, ob alle Produkte, die wir auf der Apple-Seite starten, mit den Produkten in unserer Konfiguration übereinstimmen.
2. Grenzfälle.
Wenn die Zahlung beispielsweise vollständig abgeschlossen ist, erhält der Benutzer eine Benachrichtigung, dass er die Zahlung abgeschlossen hat. Unsere Anwendung kann jedoch den Scheck nicht finden, der aufgrund dieser Zahlung gefälscht werden soll. Das Risiko liegt in der Tatsache, dass wir den Scheck selbst mit Hilfe einer Hintertür anbringen und einen solchen Fall natürlich nicht nachverfolgen können. Um dieses Risiko irgendwie auszugleichen, führen wir nach der Freigabe End-to-End-Überprüfungen über die Sandbox oder eine echte Zahlung durch.
3. Unfaire Fälschung oder Betrug.
Nachdem Sie diesen Artikel gelesen haben, könnten Sie denken, dass Sie, da Badoo gefälschte Schecks verwendet, etwas Falsches an uns anhängen und den Service kostenlos nutzen können. Damit dieses Risiko nicht eintritt, unterschreiben wir alles mit unserem eigenen Zertifikat und beschränken die Verwendung von Moks und Fake Checks auf Funktionstests, die nur in unserer Entwicklungsumgebung ausgeführt werden.
4. Ändern Sie das Format des Schecks.
Dies ist das schwerwiegendste Risiko. Das Format eines Schecks kann geändert werden, wenn Apple etwas ändert, ohne uns zu warnen. Wir hatten einen solchen Fall: Beim Wechsel zu iOS 11 änderte sich das Format der Prüfung vollständig. Wir haben einen gefälschten Check auf unserem Server generiert und im Test verwendet. Bei uns war alles perfekt: Alle Felder sind vorhanden, alles ist wunderbar, alles wird verarbeitet. Aber als wir zum realen System wechselten, funktionierte nichts. Felder, die für die Prüfung von Bedeutung waren, existierten einfach nicht mehr.
Wie kann dieses Risiko kompensiert werden? Erstens schließen wir die Möglichkeit eines End-to-End-Tests der Sandbox vor der Veröffentlichung und einer tatsächlichen Zahlung nach der Veröffentlichung nicht aus. Jetzt befinden wir uns in der aktiven Phase eines Projekts zur Überprüfung von Benachrichtigungen, wenn wir versuchen, alle Überprüfungen, die wir von der Produktion erhalten, danach zu klassifizieren, ob wir verstehen, was es ist oder nicht. Wenn die Antwort Nein lautet, beginnen wir, alles manuell zu verarbeiten, um zu sehen, was sich geändert hat, was falsch ist und was in unserem System geändert werden muss.
Ergebnis
Betrachten Sie die Hauptvorteile, die wir durch die Anwendung der Moki-Methode und des gefälschten Objekts erzielen konnten.
Preiswerte, schnelle und stabile Automatisierung kostenpflichtiger Dienste unter iOS
Zusammen mit dem manuellen Testteam von iOS (besonderer Dank an Colin Chan) konnten wir mehr als 150 automatische Tests für Zahlungen schreiben. Dies ist eine ziemlich große Abdeckung für einen Bereich der Anwendung.
Dank der Parallelisierung können wir das Ergebnis in nur 15 bis 20 Minuten in jedem Zweig des iOS-Client-Entwicklers oder Abrechnungsserver-Entwicklers erhalten. Vor der Automatisierung dauerte das manuelle Testen dieses Bereichs durch eine Person acht Stunden.
Wir können auch die überwiegende Mehrheit der Testfälle testen, indem wir den Mock Payment Provider über das Moki so einrichten, wie wir es benötigen. Mithilfe von Mooks haben wir gelernt, wie Sie die Produktprüfung deaktivieren und Fälle simulieren, in denen die Prüfung teilweise durchgeführt wird. So haben wir Fälle eröffnet, die wir vorher prinzipiell nicht testen konnten.
Funktionale Regression bei der Entwicklung neuer Funktionen
Die Automatisierung funktionierte sehr gut in den Fällen, in denen der Entwickler bei der Arbeit an einer neuen Funktion die alte Funktionalität beeinträchtigte. Wir hatten ein Beispiel, als ein Entwickler eine komplexe Funktion mit Caching ausführte und unsere automatischen Tests durchführte. Einige von ihnen sind irrtümlich gefallen. Er sah es und reparierte es. Dann startete er die Autotests erneut - und wieder fiel etwas. Infolgedessen führte er eine Reihe von Iterationen durch, bis auf der Anwendungsseite alles normal zu funktionieren begann.
Funktionale Regression beim Zahlungs-Refactoring
Die vielleicht erfolgreichste und effizienteste Automatisierung, die möglich ist, findet im Bereich des Code-Refactorings statt. In diesem Fall ändert sich nur die interne Implementierung - der Autotest-Code muss nicht geändert werden. Die Benutzeroberfläche ändert sich in keiner Weise und Autotests können effizient gesteuert werden.
Testen experimenteller Funktionen von Apple: Kulanzfrist
Ein ähnliches System ist vollständig austauschbar, wenn Sie neue Integrationen testen, die noch nicht in der Sandbox implementiert sind. So war es auch mit der Gnadenfrist. Diese Funktionalität befindet sich nicht in der Sandbox. Die Nachfrist für Apple steht noch nicht allen zur Verfügung. Dies ist ein Pilotprojekt, das Badoo mit Apple umsetzt. Um dies mit einer Nachfrist zu überprüfen, mussten wir hier einen solchen JSON-Code hinzufügen:
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. Nachfrist für ein AbonnementWir haben das sehr einfach in wenigen Sekunden gemacht. In unserem System konnten wir unsere Reaktion auf eine neue Funktion testen. Jetzt führen wir diese Funktionalität auf dem Produkt aus.
Produktqualitätsprüfung in Zusammensetzungsmethoden
Als Ergebnis unserer Forschung konnten wir eine Methode beschreiben, die Rauschen durch externe Abhängigkeiten eliminiert. Dies half Client-Entwicklern bei der Entwicklung von Funktionen, Fehler in einem frühen Stadium zu finden.
Aber denken Sie nicht, dass wir mit dieser Methode alles testen konnten. Um alles zu testen, ist es besser, eine Zusammensetzung von Methoden zu verwenden: Testen mit einer echten Karte auf dem Produkt, Testen in der Sandbox, Testen von Mokes und gefälschten Objekten, Testen von Einheiten und Integration. Bitte denken Sie an das Gleichgewicht der Testpyramide und versuchen Sie nicht, alle Probleme mit einer Methode zu lösen. Dies kann zu einer traurigen Automatisierung in der Sandbox, zu traurigen manuellen Tests mit einer echten Karte aller Fälle und zu vielen anderen schwerwiegenden Fehlern genau dort führen, wo ihr Erscheinungsbild am schmerzhaftesten ist.
Fazit
Als Ergebnis unserer Forschung haben wir eine kostengünstige, schnelle und stabile Methode erhalten, um nicht nur kostenpflichtige Dienste unter iOS zu testen, sondern auch alle Komponenten, die als „Black Box“ in die Anwendung eingebettet sind. Jetzt implementieren wir bei Badoo diese Methode zum Testen auf kostenpflichtigen Android-Anbietern (Global Charge, Boku, Centili), die instabile Sandboxen oder andere Einschränkungen aufweisen. Wir verwenden die Moki-Methode auch zum Testen von Werbung, Streaming und Geolocation.
Es ist erwähnenswert, dass der Prozess der Einführung einer neuen Methode nicht schnell war. Ich musste mit vier Teams verhandeln: iOS QA, iOS Dev, Billing QA, Billing Dev. Nicht jeder wollte aus Angst vor Risiken auf eine neue Methode umsteigen. Manchmal war es eine dogmatische Folge: Viele Jahre lang haben wir im Sandkasten getestet, und die Hauptkraft, die das Dogma zerstören konnte, war der Wunsch der Abrechnungstester und der iOS-Plattform, die Situation zu ändern und Qualen loszuwerden. Später erkannten die Entwickler solche Vorteile dieser Methode wie eine genaue Diagnose (wir konnten keine Fehler in der Sandbox finden, aber Fehler unseres Clients oder Servers), Flexibilität beim Einrichten der Komponente (wir konnten negative Fälle auf Integrationsebene leicht testen) und natürlich die Antwort 30 Minuten auf einem Zweig mit entwickeltem Code.
Vielen Dank an alle, die bis zum Ende gelesen haben. Vielen Dank an alle, die an diesem Projekt mitgewirkt haben. Besonderer Dank geht an diese Leute:
- Peter Kolpashchikov ist ein iOS-Entwickler, der Moki auf der Client-Seite entwickelt und ein PPP-Konzept entwickelt hat.
- Vladimir Solodov - Billing QA, der mit der QA-API beim Generieren von gefälschten Schecks und Checkout vom Abrechnungsserver geholfen hat;
- Maxim Filatov und Vasily Stepanov - Billing Dev Team, die beim Abrechnungsservercode geholfen haben;
- iOS Dev Team - Entwickler, die unsere Zahlungen in einem neuen Konzept umgestalten konnten, um die Verwendung von Mokas zu ermöglichen;
- Das iOS QA Team ist ein großartiges Testteam, das eine Reihe von Autotests geschrieben hat.
- Billing QA Team - Tester, die bei der Erforschung von Problemen geholfen haben.