[譯] 為你的 iOS App 構建分離測試

iWeslie發表於2019-05-04

[譯] 為你的 iOS App 構建分離測試

分離測試是為應用提供哪種方案對於給定目標表現更優決策的方法。

我們為應用的使用者以隨機的方式分發變數或行為不同的方案,通過收集資料並統計分析,確定哪個方案表現的更好。

本文旨在提供一種結構化組織構建 App 的簡單方法,以便你可以在使用分離測試時能獲得整潔而可擴充套件的程式碼。

本文提供了一些技巧和示例,你可以把它當作實際應用下的指南。

一般性問題

使用分離測試(也稱為 A/B 測試),我們擁有無限的測試可能性。但總的來說,我們可以按以下順序對分離測試所需進行的修改進行分組:

  1. 內容變更:僅更改指定檢視中的特定部分或根據給定的測試新增或刪除特定內容。

  2. 設計變更:測試顏色、排版或佈局等變化會如何影響使用者的行為。

  3. 行為變更:根據拆分組來更改按鈕操作或螢幕顯示的行為。

但其中問題在於,所有這些類別中可能會出現大量重複的程式碼。

我們需要為測試建立一種易於維護的程式碼結構,這是因為我們需要不斷新增新測試或刪除修改舊測試,因此需要考慮它的可擴充套件性。

建立拆分離測試管理器

我們將嘗試建立一個通用解決方案並將其用於上述的變更類別。

首先我們建立一個協議來定義拆分測試物件必須符合的規則:

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

相關文章