在iOS上自动测试付费服务

对于那些对iOS自动化主题感兴趣的人,我有两个消息-好消息和坏消息。 很好:在付费服务的iOS应用程序中,仅使用了一个集成点-应用程序内购买(应用程序中的内置购买 )。 坏:Apple不提供任何工具来自动执行测试购买。

在本文中,我建议您和我寻求一种超越Apple优缺点的通用自动化方法。 对于将黑匣子第三方服务集成到其应用程序中的任何人(广告,流媒体,位置管理等),本文将非常有用。通常,此类集成非常难以测试,因为无法灵活地配置第三方服务来测试应用程序。



我叫Victor Koronevich,我是Badoo的高级测试自动化工程师。 从事移动自动化已有十多年了。 我们与我的同事弗拉基米尔·索洛多夫(Vladimir Solodov)一起在Heisenbug会议上做了这份报告 他还帮助我准备了这段文字。

在上一篇文章中,我们描述了Badoo用于测试与支付提供商的集成的方法,而我们已经有70多种方法。在本文中,我们将更多地讨论如何在iOS应用程序中实现对付费服务的稳定且廉价的自动化测试。

让我们从对研究的一般描述开始:

  1. 问题定义
  2. 问题陈述
  3. 解决方案编号1。 苹果沙盒
  4. 决定号2。 函数模拟方法和伪对象的使用
  5. 评估决策:主要风险
  6. 结果
  7. 结论

问题定义


当自然需要出现时,需要进行自动化。 这个时刻什么时候来的?

Badoo应用程序中有很多免费功能,但付费功能为用户提供了更多选择。 他们以两种方式获得贷款:用于贷款-Badoo的内部货币-或通过购买高级认购权。 对于一定数量的积分,您可以将您的个人资料在搜索结果中的位置提高到第一位,向其他用户赠送礼物,等等。 付费订阅的有效期为一定时间,并一次提供多个选项:打开隐身模式,查看对您表示同情的人,取消投票的结果等。

这些功能逐渐出现在Badoo中。 几年前,我们仅在iOS应用程序中手动测试了付费服务。 但是随着功能和新屏幕的出现,手动测试花费了越来越多的时间。 对应用程序进行更改的要求来自不同方面:客户端的开发人员,服务器端的开发人员,甚至Apple提供程序本身。 对于一名测试人员而言,一次测试迭代大约需要八个小时。 在30分钟内就不可能在其分支机构获得快速反馈,最终可能会对产品的竞争力产生负面影响。

我们希望尽快获得测试结果。 他们遇到了一个问题:如何廉价地在我们的iOS应用程序中组织付费服务的回归测试,以便获得快速,稳定的结果?

问题陈述


因此,考虑到我们交付最终产品的过程的细节和团队的规模,我们希望:

  • 测试客户端应用程序中的所有购买(一次性付款和订阅);

  • 每天重复测试10至20次;
  • 在不到半小时的时间内获得150个测试脚本的测试结果;
  • 消除噪音;
  • 不管其他运行的结果如何,都能够在开发人员代码的特定分支上运行测试。

现在我们已经制定了任务,是时候开始进入工程师及其解决方案的美好世界了。

解决方案编号1。 苹果沙盒


首先,我们开始在Apple文档中寻找有关组织自动测试付费服务的信息。 他们什么也没找到。 自动化支持非常有限。 如果出现问题,则很难使用建议的工具来设置自动化(至少让我们回想起UIAutomation以及iOS Simulator的第一个xcrun simctl实用程序出现的时间),并且您必须寻找工程解决方案,包括在开源部分中。

在用于测试付费服务的Apple文档中,您只能找到Apple Sandbox 。 目前尚不清楚如何将此沙箱与自动化联系起来,但我们决定认真研究此解决方案。 Android沙箱稳定的事实使我们充满信心,到那时我们已经在Android上成功编写了测试。 也许苹果沙盒会一样好?

但是,当我们使用此沙箱实施自动测试时,我们会全神贯注。 让我们快速解决主要问题。

1.测试用户群


自动化的主要限制是测试用户池中内容的功能,这应确保自动测试启动的独立性。

要仅运行一个自动购买的订购,我们需要:

  1. 在沙箱中获取新用户进行授权;
  2. 在模拟器上更改当前链接的Apple ID;
  3. 使用Badoo登录到Badoo
  4. 进入订购购买屏幕并选择产品;
  5. 确认购买并通过Apple ID登录;
  6. 确保购买成功;
  7. 发送Badoo用户进行清洁;
  8. 从订阅中清除沙箱用户。

如果您尝试在下一个测试中立即使用同一用户,则将无法购买第二个订阅。 您需要等到第一个订阅“变质”,或者在设置中退订。 如我们在第一篇文章中所述,沙盒具有特定的订阅有效期。 如果您购买“一个月”的订阅,则必须等待五分钟才能自动将其关闭。 退订过程本身也不快。

因此,对于同一测试的新运行,我们将需要等待直到订阅结束,或者接受另一个“干净”的用户。 如果我们要彼此独立地同时运行两个测试,则需要在池中至少有两个沙箱用户。 因此,要在100个线程中并行运行100个自动测试,我们需要100个不同的用户。

现在让我们想象一下,我们正在两个代理上进行自测试,每个代理可以在100个线程中运行它们。 在这种情况下,我们至少需要200个用户!

2.“错误”通知


好吧,这到底是不是在开玩笑! 我们组织了一个用户池,并开始观察测试如何运行。 他们沿着这条路走了,但是大多数-由于我们未知的新原因。 我们开始理解并意识到,在授权,确认购买并在沙箱中以用户身份工作时,App Store会发送警报:例如,要求新的用户名和密码,通过单击“确定”按钮确认授权,通过“确定”按钮提供有关内部错误的信息。 有时它们出现,有时不出现。 如果它们出现,则总是以不同的顺序。



如何在自动测试中将可疑错误简单地忽略掉? 如果真的出现错误,那我该怎么办? 该区域自动成为我们的“盲区”,我们不得不为可能从App Store到达的所有警报编写特殊的处理程序。

所有这些使测试变慢了:

  • 警报可能到达测试方案的不同步骤,从而破坏了测试的主要思想-可预测的测试方案; 我们必须添加一个错误处理程序,以期望可能出现一系列已知的被忽略警报。
  • 有时会收到新的警报变体或发生其他错误,因此我们不得不重新启动失败的测试; 这增加了所有测试的运行时间。

3.有考试吗?


因此,池中的用户被阻止,然后清除了n分钟。 我们在120个线程中运行测试,并且池中已经有很多用户,但这还不够。 我们制作了用户管理系统,制作了警报处理程序,然后IT发生了。 对于任何测试用户来说,沙盒在几天之内都无法使用。

没有人期望这一点。 这是耐心圣杯中的最后一根稻草,它最终杀死了苹果沙盒的热爱,使我们走上了善与恶的道路。 我们意识到我们不需要这种自动化,并且我们也不想再因这个危险的决定而遭受痛苦。

决定号2。 函数模拟方法和伪对象的使用


因此,我们在Apple的沙箱中饮用了自动化方面的问题。 但是不要以为在移动世界中所有事情都是完全糟糕的。 在Android上,沙箱更加稳定-您可以在其中运行自动测试。

让我们尝试为iOS寻找另一种解决方案。 但是怎么看? 在哪里看? 让我们看看测试和软件开发的历史:Apple的疯狂世界发生了什么? 在自动化和软件开发领域写过很多书并获得权威的人们怎么说?

我立即想起了Gerard Mesaroche( Martin Fowler撰写)撰写的著作“ xUnit测试模式:重构测试代码”,在我看来,这是任何至少知道一种高级编程语言并想要自动化的测试人员的最佳书籍之一。 。 本书中有几章专门与应用程序的其他组件(即我们的“黑匣子”)隔离地测试SUT,可以为我们提供帮助。

1.摩卡咖啡和假冒产品简介


应当指出,在自动测试的世界中,测试双打,测试桩,测试间谍,模拟对象,伪对象,虚拟对象的概念之间没有公认的界限。 您必须始终考虑作者的术语。 我们只需要来自测试双打世界的两个概念:一个模拟函数和一个伪对象。 这是什么 为什么我们需要这个? 我们对这些概念进行了简要定义,以使我们之间没有分歧。

假设我们有一个应用程序和一个内置的组件,对我们来说这是一个“黑匣子”。 在应用程序内部,我们可以通过访问此组件来调用函数并获取这些函数的结果。 根据结果​​,我们的应用程序以特定的方式做出反应。 有时,函数执行的结果可能是带有一堆反映真实用户数据的字段的整个实体。

将函数替换为可返回所需结果的任何其他函数,我们称其为模拟,或简称为模拟。 这些功能可能具有相同的签名,但是它们是两个不同的功能。

并且,将通过功能获得的实体替换为假实体(在字段中包含必要的数据,有时甚至是损坏的数据)将被称为假对象的实现。 您可以在我上面提到的书中或在其他任何有关测试和软件开发的摘要中阅读有关此内容的更多信息。

最后,让我们强调一下使用模拟函数和伪造对象的一些功能:

  1. 为了使功能更丰富,您需要访问源代码,并在开发人员级别从内部了解应用程序如何使用组件。
  2. 为了实现伪造的对象,您需要了解真实对象的结构。
  3. 使用模拟功能可通过组件灵活配置应用程序。
  4. 使用伪造的对象可使您赋予实体任何属性。

moki和伪对象方法非常适合隔离应用程序中组件的操作。 让我们看看如何应用此方法来解决我们的问题,其中App Store将是其中的组成部分。 由于使用此方法的特殊性,我们首先需要转向使用组件研究应用程序工作的性质,然后转向制作特定密钥和伪造对象的技术实现。

2.实际购买如何发生


在开始描述系统各部分的交互之前,让我们重点介绍主要参与者:

  • 应用程序用户-对应用程序执行操作的任何参与者,可以是一个人,也可以是执行必要指令的脚本;
  • 应用程序(在本例中,我们使用安装在iOS模拟器中的Badoo iOS应用程序);
  • 服务器-负责处理来自应用程序的请求并在没有客户端请求的情况下发送响应或异步通知的参与者(在这种情况下,我们指的是一台抽象的Badoo服务器,以简化结构);
  • App Store是一个对我们来说是一个“黑匣子”的参与者:我们不知道它在内部的排列方式,但是我们知道它在应用程序( StoreKit框架 )中处理购买的公共接口,并且知道如何在Apple服务器上检查数据。

让我们看看如何购买。 整个过程可以在图中看到:


图1. App Store上的付款方案

我们将逐步描述参与者的主要行为。

1.起点是所有演员在打开带有产品列表的屏幕之前的状态。

这个屏幕是什么,我们如何进入屏幕?

假设用户找到了一个有趣的人,打开了他的个人资料,写了一条消息,想发送礼物。 发送礼物是一项付费服务​​。 用户可以将个人资料滚动到发送礼物的部分,也可以立即从聊天中选择礼物。

如果用户选择了礼物并且帐户中没有钱,那么他将看到要购买的不同贷款包列表(“付款向导”)。 在我们的示例中,起点是礼物清单。 在图中,我们可以在显示用于购买贷款或认购的产品列表之前的任何屏幕上考虑这一点。

2.打开产品列表。

例如,我们位于礼物清单的起点。 用户在应用程序中选择礼物之一。 该应用程序向我们的服务器发出请求,以获取可能的产品ID贷款包列表(100、550、2000、5000)。 服务器将此列表返回给应用程序。

接下来,应用程序将收到的产品ID列表发送给App Store actor(用于Apple服务器的StoreKit系统iOS框架)以进行验证。 它返回经过验证的产品的列表-结果,该应用程序向用户显示带有图标和价格的贷款包的最终列表。

3.产品选择和收据生成。

用户选择付费产品。 App Store要求通过Apple ID证明购买和授权。 成功的用户授权后,控制权将转移到应用程序。 应用程序正在等待在其自己的程序包中生成收据。 用户此时看到的是阳光,它锁定了屏幕。 使用Bundle类的appStoreReceiptURL方法可以理解生成的收据。 App Store生成支票后,应用程序从其程序包中选择支票,并将带有支票和用户数据的请求发送到Badoo服务器。

4.检查Badoo服务器上的支票。

Badoo服务器一收到支票和用户数据,便立即将它们发送回Apple服务器端以执行第一个验证周期。 这是苹果公司的建议之一。 然后,在此第一个验证周期中,服务器接收有关订阅的当前状态的信息。

5.从服务器发送推送通知(推送通知)。

Badoo服务器在Apple验证后再次处理收到的信息,并向应用程序发送响应和推送通知。

6.在应用程序中推送通知。

如果是购买贷款,则用户在应用程序中的余额将立即更改,并且他会在聊天室中看到发送的礼物。 如果这是订购购买,则用户应等待激活订购的最终推送通知。

3.确定依赖关系和测试循环



为了进一步讨论,我们引入了另外两个概念-外部依存关系和测试电路。

外部依赖


外部依赖性是指与组件的任何交互,对我们而言这是“黑匣子”。 在这种情况下,App Store以iOS系统框架(StoreKit)的形式充当此类组件,我们的iOS应用程序可与之一起工作,而Apple服务器则在其中进行验证请求。

在实际条件下管理这些依赖关系是不可能的,应用程序被迫响应黑匣子的输出信号(见图2)。

我们有三个外部依赖项:

  1. 检查StoreKit产品。
  2. 接收和替换购买收据。
  3. 在Badoo服务器上检查支票。


图2.外部依赖关系

测试电路


测试电路-这些是我们在测试过程中将通过并检查的路径的各个部分。


图3.测试循环

我们消除依赖关系的工作目标是建立一个尽可能接近真实路径的测试电路,并允许您排除所有外部依赖关系并将控制权转移到您的身边。

我们依次考虑每个依赖项。

4.隔离依赖项:技术实施


在我们公司中,为了实现付款,我们采用了基于付款提供商界面的PPP概念。 这是与我们的应用程序内的App Store actor(StoreKit)进行交互的主要界面,它有两种主要方法:

  1. 准备是负责检查产品的方法;
  2. makePayment是一种处理应用内购买的方法。

iOS上的所有付款都根据此概念进行了重构,这使我们能够获得一个简单方便的类模拟付款提供商。 这是用于与应用程序内StoreKit行为的便捷副本进行交互的主要界面。 “便捷复制”是什么意思? 该提供程序具有可实现我们所需功能的prepare和makePayment方法的模拟。 让我们看一段代码示例,说明我们如何集成moki。

依赖号1。 检查StoreKit产品


要检查产品列表,请使用prepare函数,该函数返回已检查产品的列表。 我们可以使用模拟功能,在该模拟功能中,我们关闭检查并返回经过完全验证的产品入站列表。 因此,将消除依赖性。


图4.第一个依赖消除方案

付款应用程序是我们应用程序中体系结构的最顶层。 它反映了应用程序中可能的提供程序的接口。 可以在Mock Payment Provider类中找到用于实现mok的代码。

public class MockPaymentProvider: PaymentProvider { public static var receipt: String? public static var storeKitTransactionID: String? public func prepare(products: [BMProduct]) -> [BMProduct] { return products } ... } 

清单1.模拟客户端检查

在模拟支付提供商处,我们可以看到prepare方法的实现。 moka的魔力非常简单:该方法跳过了StoreKit端的产品检查,它只是返回了一个传入的产品列表。 prepare的实际实现如下所示:

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

清单2.实际商店付款提供者

2号依赖 接收和替换购买收据


第二种依赖性更加复杂:我们需要先删除授权,以免保留用户帐户池,然后以某种方式获取支票本身。 我们可以简单地删除授权表:


图5.付款时删除授权表单

支票不是那么简单。 有很多问题:

  1. 如何提前获得合适产品的收据?
  2. 如果我们确实收到了支票,那么何时以及如何将其附加到应用程序中?

在此,演员“用户”具有新角色-质量检查。 运行测试时,我们不仅可以单击界面上的按钮,还可以调用测试框架的API方法(模拟用户操作的方法)和REST API服务(可以从内部Badoo服务中执行魔术的方法)。 Badoo我们使用了功能非常强大的QA API工具(您可以在以下链接中找到其所有功能: https : //vimeo.com/116931200 )。 是他帮助我们进行了测试,并在Badoo的服务器端检查了正确的产品。 Badoo服务器是生成检查的最佳位置:检查有加密和解密的功能,因此服务器知道有关此数据结构的所有信息。

收到假支票后,我们可以将其放入应用程序侧的后门 。 接下来,应用程序会将伪造的支票以及用户数据发送到我们的服务器。


图6.接收方案

这在技术上如何成为可能?

1.要在应用程序中设置伪造支票,我们可以使用后门将伪造支票保存在收据MockPaymentProvider字段中:

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

清单3.假支票后门

2.该应用程序能够通过MockPaymentProvider接受我们的支票,其中我们使用了makePayment模拟和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) } } } 

清单4.用伪造支票呼叫购买处理Moka

3.收到假支票

为了获得伪造的支票,我们在服务器上使用了该方法(请参见清单5)。 它使用带有数据的默认数组来生成检查数据,并将特定产品所需的数据添加到其中。

 $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 ); 

清单5.检查生成的服务器部分

要重复真实支票的结构,必须使用证书对应用程序发送的自定义支票进行加密。 我们使用工作证书而不是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); } 

清单6.用证书签署支票的方法

4.结果,在测试中我们得到:

 (/       "((\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 

清单7. Cucumber框架的Gherkin测试步骤

依赖号3。 在Badoo服务器上检查支票


要删除第三个依赖项,您需要摆脱服务器上的检查验证。 重要的是要记住,验证是分两个阶段完成的。 在第一阶段,根据签名和证书对支票进行身份验证。 在第二个-支票被发送到App Store。 如果在此阶段成功通过验证,我们将收到可以处理的解密支票。


图7.删除服务器验证

首先,服务器在父类的verifyReceiptByCert方法中执行检查的初始验证。 这将使用App Store证书验证签名。 如果是假支票,则此验证将失败,因为它是由我们的证书签名的,我们将使用本地证书verifyReceiptByLocalCert调用该验证方法。 在此方法中,我们将尝试使用本地证书对支票进行解密,如果成功,则将解密结果放置在子类的local_receipt内部字段中(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; } 

清单8.初始验证

在辅助验证(verifyReceipt)期间,我们获取getLocallyVerifiedReceipt子类的local_receipt字段的值。 如果它不为空,那么我们将其值用作验证的结果。

如果该字段为空,则我们从父类( parent :: verifyReceipt)调用辅助验证。 在此,我们向App Store发出请求,要求其进行验证。 两种情况下的验证结果都是解密检查。

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

清单9.二级验证

5.视频测试运行:购买贷款和订阅


测试编号1。 订阅购买


什么时候
我以新用户的身份使用照片登录到应用程序

我生成了一个新的为期一个月的订阅结算支票

我去我的个人资料
然后
我确定订阅已禁用
什么时候
我打开产品清单

我购买一个月的订阅套餐
然后
我检查成功的购买通知

我确保订阅已激活

试运行视频:


测试编号2。 买贷款和送礼物


什么时候
我以新用户的身份使用照片登录到应用程序

我在个人资料中添加了10个学分

我生成了550个学分的新信用检查

我创建一个新用户Leela

里拉为我投票赞成

我去附近的人并打开Leela个人资料

我为里拉投赞成票
然后
我查看比赛页面
什么时候
我选择发送定期礼物
然后
我在付款画面上查看包裹清单
什么时候
我选择购买550学分
然后
我检查成功的购买通知

我确定Leela收到了聊天礼物


试运行视频:



评估决策:主要风险


删除外部依赖项会带来某些风险。

1.配置错误。

由于不在我们这边进行验证,因此我们可能在Apple方面错误地配置了我们的产品。 为了防止错误,我们编写了一个单独的服务器端单元测试,该测试检查我们在Apple端启动的所有产品是否都与配置中的产品匹配。

2.边界案件。

例如,当付款完全完成时,用户会收到一条通知,告知他已完成付款,但是我们的应用程序找不到因付款而伪造的支票。 风险在于我们自己在后门的帮助下附上支票,而我们自然无法跟踪这种情况。 为了以某种方式弥补这种风险,我们使用沙盒或发行后的实际付款进行端到端检查。

3.不正当的伪造或欺诈。

阅读本文后,您可能会认为,由于Badoo使用了伪造的支票,因此您可以在我们身上附上伪造的东西并免费使用该服务。 为了避免这种风险的发生,我们使用自己的证书对所有内容进行签名,并限制只在我们的开发环境中运行的功能测试使用mok和伪造支票。

4.更改支票的格式。

这是最严重的风险。 如果Apple在未通知我们的情况下进行了更改,则可以更改支票的格式。 我们有这样一种情况:切换到iOS 11时,支票的格式完全改变了。 我们在服务器上生成了伪造的支票,并在测试中使用了它。 一切对我们来说都是完美的:所有领域都准备就绪,一切都很棒,一切都在处理中。 但是当我们切换到实际系统时,没有任何效果。 支票上重要的字段根本不复存在。

如何弥补这一风险? 首先,我们不排除在发布之前对沙盒进行端到端测试以及在发布之后进行真实付款的可能性。 现在,我们正处于检查通知项目的活动阶段,当我们尝试根据对产品的了解还是不了解,对从生产中收到的所有检查进行分类时。 如果答案是否定的,那么我们将开始手动处理所有内容,查看已更改的内容,错误的内容以及系统中需要更改的内容。
冒风险
原因
如何赔偿
错误的配置
删除支票
服务器上的单元测试
边缘情况
(未提供支票)
使用后门
端到端支票(沙盒和真实付款)
欺诈性欺诈
服务器上的通知和检查生成
自己的证书
变更支票格式
服务器上的通知和检查生成
验证实际通知并检查产品(新项目),
端到端支票(沙盒和真实付款)

结果



考虑一下通过应用moki方法和伪造对象而获得的主要优点。

iOS上收费服务的廉价,快速和稳定的自动化


与iOS手动测试团队(特别感谢Colin Chan一起),我们能够编写150多种付款自动测试。 这对于应用程序的一个区域来说是相当大的覆盖范围。

由于并行化,我们可以在15至20分钟内在iOS客户端开发人员或计费服务器开发人员的任何分支上获得结果。 在自动化之前,一个人进行的手动测试需要八个小时。

我们还可以按照需要通过Moki设置模拟付款提供商来测试绝大多数测试用例。 在木匠的帮助下,我们学习了如何关闭产品检查并在部分执行检查时模拟案例。 因此,我们打开了以前原则上无法测试的案例。

新功能开发中的功能回归


在开发人员开发新功能的过程中影响旧功能的情况下,自动化效果很好。 我们有一个示例,说明开发人员通过缓存执行了一项复杂功能并运行了自动测试。 其中一些错误。 他看到并修复了它。 然后,他再次重新启动了自动测试-再次失败了。 结果,他进行了一系列迭代,直到一切都开始在应用程序端正常工作为止。

付款重构中的功能回归


可能最成功,最有效的自动化发生在代码重构领域。 在这种情况下,仅内部实现会发生变化-无需更改自动测试代码。 UI不会发生任何变化,并且可以有效地驱动自动测试。

测试Apple的实验功能:宽限期


当您测试沙箱中尚未实现的新集成时,一个类似的系统可以完全互换。 宽限期也是如此。 此功能不在沙盒中。 苹果公司的宽限期尚未提供给所有人。 这是Badoo与Apple一起实施的一个试点项目。 为了检查宽限期,我们需要在此处添加这样的一段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 }] 

清单10.订阅的宽限期

我们在短短几秒钟内就很容易做到了。 在我们的系统中,我们能够测试对新功能的反应。 现在,我们在产品上运行此功能。

组成方法中的产品质量测试


作为我们研究的结果,我们能够描述一种消除外部依赖噪声的方法。 这有助于客户开发人员在开发功能的过程中尽早发现错误。

但是不要以为我们可以用这种方法测试所有东西。 要测试所有内容,最好使用多种方法:使用产品上的真实卡进行测试,在沙箱中进行测试,模拟和伪造对象的方法,单元和集成测试。 请记住测试金字塔的平衡,不要试图用一种方法解决所有问题。 这可能会导致沙箱中的自动化工作变得很糟糕,使用所有情况下的真实卡片进行令人讨厌的手动测试,以及在外观最痛苦的地方出现许多其他严重错误。

结论


经过研究,我们得到了一种廉价,快速,稳定的方法,不仅可以测试iOS上的付费服务,还可以测试应用程序中作为“黑匣子”嵌入的任何组件。 现在,在Badoo,我们正在为具有不稳定沙箱或任何其他限制的Android付费提供商(Global Charge,Boku,Centili)实施这种方法进行测试。 我们还使用moki方法测试广告,流媒体和地理位置。

值得一提的是,引入新方法的过程并不很快。 我必须与四个团队进行谈判:iOS质量检查,iOS开发,结算质量检查,结算开发。 并非每个人都担心风险,因此想切换到新方法。 有时这是一个教条式的遵循:多年来,我们在沙盒中进行了测试,而可能破坏教条的主要力量是计费测试人员和iOS平台希望改变现状并摆脱折磨。 后来,开发人员意识到了这种方法的优势,例如准确的诊断(我们无法在沙盒中找到错误,但是可以找到客户端或服务器的错误),设置组件的灵活性(我们能够轻松地在集成级别测试否定情况),当然,答案是用已开发代码的分支上30分钟。

非常感谢阅读到底的每个人。 非常感谢帮助和参与该项目的每个人。 特别感谢这些人:

  • 彼得·科帕什奇科夫(Peter Kolpashchikov)是一位iOS开发人员,曾帮助在客户端制作moki并开发了PPP概念。
  • 弗拉基米尔·索洛多夫(Vladimir Solodov)-计费质量检查人员,他协助质量检查API生成了来自计费服务器的伪造支票和结帐单;
  • Maxim Filatov和Vasily Stepanov-Billing Dev Team,负责计费服务器代码;
  • iOS开发团队-开发人员能够以新概念重构我们的付款方式,从而可以使用Mokas;
  • iOS质量检查团队是一个了不起的测试团队,编写了许多自动测试。
  • 结算质量检查小组-帮助研究问题的测试人员。

Source: https://habr.com/ru/post/zh-CN460667/


All Articles