[譯] 爲你的 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前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索