[译] 为你的 iOS App 构建分离测试

分离测试是为应用提供哪一种方案对于给定目标表现更优决策的方法。前端

咱们为应用的用户以随机的方式分发变量或行为不一样的方案,经过收集数据并统计分析,肯定哪一个方案表现的更好。android

本文旨在提供一种结构化组织构建 App 的简单方法,以便你能够在使用分离测试时能得到整洁而可扩展的代码。ios

本文提供了一些技巧和示例,你能够把它看成实际应用下的指南。git

通常性问题

使用分离测试(也称为 A/B 测试),咱们拥有无限的测试可能性。但总的来讲,咱们能够按如下顺序对分离测试所需进行的修改进行分组:github

  1. 内容变动:仅更改指定视图中的特定部分或根据给定的测试添加或删除特定内容。算法

  2. 设计变动:测试颜色、排版或布局等变化会如何影响用户的行为。数据库

  3. 行为变动:根据拆分组来更改按钮操做或屏幕显示的行为。swift

但其中问题在于,全部这些类别中可能会出现大量重复的代码。后端

咱们须要为测试建立一种易于维护的代码结构,这是由于咱们须要不断添加新测试或删除修改旧测试,所以须要考虑它的可扩展性。设计模式

建立拆分离测试管理器

咱们将尝试建立一个通用解决方案并将其用于上述的变动类别。

首先咱们建立一个协议来定义拆分测试对象必须符合的规则:

protocol SplitTestProtocol {
    associatedtype ValueType: Any
    static var identifier: String { get }
    var value: ValueType { get }
    init(group: String)
}
复制代码

value 表示一个通用值,该值将由具体的分离测试对象实现。它将对应于咱们为目标目标测试的颜色,字体或任何属性。

identifier 将做为测试的惟一标识符。

其中的 group 将表明当前正在测试的值。它能够是 abredgreen,这彻底取决于为给定测试肯定的值的命名。

咱们还将建立一个管理器,负责根据与测试标识符相关的数据库中存储的组获取拆分测试的值:

class SplitTestingManager {
    static func getSplitValue<Value: SplitTestProtocol>(for split: Value.Type) -> Value.ValueType {
        let group = UserDefaults.standard.value(forKey: split.self.identifier) as! String
        return Value(group: group).value
    }
}
复制代码

内容变动

[https://dribbble.com/shots/5805125-Book-Reading-App](https://dribbble.com/shots/5805125-Book-Reading-App)

假设咱们正在开发阅读类 App,咱们决定为用户提供免费的电子书。

咱们的营销团队决定首先经过要求用户提供如下内容来建立分离测试:

在社交媒体上分享咱们的应用

或者

订阅咱们的新闻

这两种状况都使用相同的 View Controller,但设计的一部分会随状况而改变。在咱们的 View Controller 中,咱们将建立一个 Content View 区域并在其中添加不一样的内容。

在这种状况下,咱们须要建立两个不一样的 View:一个用于社交共享,另外一个用于新闻稿,并分别添加到 View Controller 的 Content View 区域内。

首先建立一个保存 View Controller 样式的对象,并将其传递给 View Controller 的初始化器:

struct PromotionViewControllerStyle {
    let contentView: String
}
复制代码
init(style: PromotionViewControllerStyle) {
    self.style = style
    super.init(nibName: nil, bundle: nil)
}
复制代码

基本上,样式对象当前包含咱们的 PromotionViewController 中 Content View 的 xib 名称。

咱们能够建立遵循 SplitTestProtocol 的测试对象:

class EBookPromotionSplitTest: SplitTestProtocol {
    typealias ValueType = PromotionViewControllerStyle
    static var identifier: String = "ebookPromotionTest"
    var value: PromotionViewControllerStyle

    required init(group: String) {
        self.value =
            group == "social" ?
                PromotionViewControllerStyle.init(contentView: "\(TwitterView.self)")
            :   PromotionViewControllerStyle.init(contentView: "\(NewsLetterView.self)")
    }
}
复制代码

如今咱们能够根据咱们的分离测试轻松地向咱们的 View Controller 显示新闻或社交共享的内容:

@IBAction func presentNextVc(_ sender: UIButton) {
    let style = SplitTestManager.getSplitValue(for: EBookPromotionSplitTest.self)
    let vc = PromotionViewController(style: style)
    self.present(vc, animated: true)
}
复制代码
func addContentView() {
    let nib = UINib(nibName: style.contentView, bundle: nil)
    let view = nib.instantiate(withOwner: nil, options: nil)[0] as! UIView
    contentView.addSubview(view)
    view.bindFrameToSuperviewBounds()
}
复制代码

设计变动

一般,在电商 App 中,更改号召性用语的按钮设计很受欢迎,即 添加到购物车购买 按钮,它们可以更加吸引用户,从而能得到更多点击。

[https://dribbble.com/shots/5546168-Gate-B](https://dribbble.com/shots/5546168-Gate-B)

咱们老是可使用咱们须要的任何对象进行分离管理,在这种状况下,假设咱们须要一个保存购买按钮颜色值的对象:

class PurchaseButtonColorSplitTest: SplitTestProtocol {
    typealias ValueType = UIColor

    static var identifier: String = "purchase_button_color"
    var value: ValueType

    required init(group: String) {
        if group == "a" {
            self.value = UIColor.red
        } else {
            self.value = UIColor.green
        }
    }
}
复制代码

以下所示,咱们能够简单地从咱们的角度来使用它:

let color = SplitTestManager.getSplitValue(for: PurchaseButtonColorSplitTest.self)
purchaseButton.backgroundColor = color
复制代码

一样,它也能够测试任何其余属性,如字体,边距或任何其余须要根据咱们的测试进行更改的属性。

行为变动

假设咱们打算将 App 中的订阅用户分红两组:

[https://dribbble.com/shots/5058686-Potted-In-app-Purchases](https://dribbble.com/shots/5058686-Potted-In-app-Purchases)

咱们既但愿

打开 IAP 视图时显示折扣对话框

也但愿

显示没有任何对话框的默认视图

咱们将使用此示例的策略模式来处理咱们的折扣演示。

策略模式是一种设计模式,用于建立可互换的算法组,你能够在运行时从中选择所需的算法。

因为咱们的 SplitTestProtocol 包含一个通用值,咱们能够建立将该策略做为其值保存的分离测试对象:

class DiscountSplitTest: SplitTestProtocol {
    typealias ValueType = DisountStrategy
    static var identifier: String = "iap_discount_type"
    var value: DisountStrategy


    required init(group: String) {
        if group == "offer" {
            value = DefaultDiscountStrategy()
        }
        value = NoDiscountStrategy()
    }
}
复制代码

而后咱们能够根据具体策略初始化并呈现咱们的 View Controller:

init(discountStrategy: DisountStrategy) {
    self.discountStrategy = discountStrategy
    super.init(nibName: nil, bundle: nil)
}
复制代码
func presentDiscoutViewController() {
    let strategy = SplitTestManager.getSplitValue(for: DiscountSplitTest.self)
    let viewController = DiscountViewController(discountStrategy: strategy)
    self.present(viewController, animated: true)
}
复制代码

咱们如今能够轻松地将咱们的折扣责任传递给 DiscountStrategy 对象,并根据咱们的需求进行扩展,而无需更改 View Controller 里的代码:

protocol DisountStrategy {
    func presentDiscountMessage()
}

struct NoDiscountStrategy: DisountStrategy {
    // 提供处理非打折的状况
}

struct DefaultDiscountStrategy: DisountStrategy {
    // 提供处理打折的状况
}
复制代码
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(true)
    discountStrategy.presentDiscountMessage()
}
复制代码

通常性提示

当你在进行分离测试时,请务必注意如下几点:

  1. 始终使用 缓存 做为测试值,以使 App 在用户使用的时候保持一致。

  2. 在一次特定测试完成后 清理 测试代码,删除你在项目中为分离测试添加的视图,字体,图像和其余任何资源。

  3. 确保若是出现问题你能够控制而且能够 禁用 A/B 测试。

总结

分离测试(也称为 A/B 测试)对于咱们的 App 来讲是一个强大而有效的工具,但若是咱们的代码设计不严谨的话,它很容易使你的代码变得一团糟。

在本文中,咱们建立了一个能够管理分离测试逻辑的通用解决方案。同时还提供了一些真实的 App 示例和实用技巧,以便你能够在给你的 iOS App 进行分离测试的时候参考。

你能够在 medium 上关注我,我还写了不少篇 iOS 的高级技巧类文章。

若是你有任何问题或者意见,请给我发送电子邮件 arlindaliu.dev@gmail.com


编者注:打算准备深刻研究一些代码吗?你能够浏览 Fritz 的 GitHub 主页。你将找到一些流行的对手机优化过的开源机器学习和深度学习的模型,你能够用它们来构建你本身的 ML 驱动的 iOS 和 Android App 的训练脚本,同时还有一些项目模板和工具。

你能够在 Slack 上加入咱们以得到技术支持,你也能够跟咱们分享你的工做,或者与咱们探讨移动端开发与机器学习方面的问题。同时,你能够关注咱们的 TwitterLinkedIn 来获取全部最新内容,更多来自移动机器学习世界的东西。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索