簡體   English   中英

從另一個模塊模擬 SwiftUI 視圖

[英]Mock a SwiftUI view from another module

我正在嘗試測試一個 SwiftUI 視圖,該視圖在其主體中具有來自另一個模塊的子視圖:

import SwiftUI
import Abond

struct ProfileView: PresentableView, LoadedView {
    @State var isLoading = true

    public var body: some View {
        Load(self) {
            AbondProfile(onSuccess: self.onSubmitSuccess)
        }
    }

    func load() -> Binding<Bool>  {
        ProfileApi.getProfileAccessToken() { result in
            switch result {
            case .success(let response):
                Abond.accessToken = response.accessToken
            case .failure(let error):
                print("error getting token")
            }
            isLoading = false
        }
        return $isLoading
    }

    func onSubmitSuccess() {
        print("success")
    }
}

我的問題是:如果我想在沒有構建實際ProfileView視圖的情況下測試AbondProfile的生命周期,有沒有辦法模擬它? 如果這是一種普通方法,我會注入一個依賴項 object,但我不知道如何將其轉換為結構初始化程序。

Abond是一個Swift Package,所以我不能修改AbondProfile。 而且我更希望能夠對我的視圖代碼進行盡可能少的更改來測試它。 我正在使用 XCTest。

正如 David Wheeler 所說,“計算機科學中的任何問題都可以通過另一個層次的間接性來解決。”

在這種情況下,一種解決方案是通過泛型類型參數間接引用AbondProfile 我們在AbondProfile ProfileView

struct ProfileView<Content: View>: PresentableView, LoadedView {
    @State var isLoading = true
    @ViewBuilder var content: (_ onSuccess: @escaping () -> Void) -> Content

    public var body: some View {
        Load(self) {
            content(onSubmitSuccess)
        }
    }

    blah blah blah
}

如果我們提供使用ProfileView的默認初始化程序,我們不必更改 ProfileView 的當前AbondProfile

extension ProfileView {
    init() where Content == AbondProfile {
        self.init { AbondProfile(onSuccess: $0) }
    }
}

struct ProductionView: View {
    var body: some View {
        ProfileView() // This uses AbondProfile.
    }
}

在測試中,我們可以提供一個模擬視圖:

struct TestView: View {
    var body: some View {
        ProfileView { onSuccess in
            Text("a travesty of a mockery of a sham of a mockery of a travesty of two mockeries of a sham")
        }
    }
}

我接受了另一個答案,因為它是一個更合適的解決方案,但我發現它實際上可以重新定義測試文件中的結構:

import XCTest
import Abond
import SwiftUI

// Mock for Abond.AbondProfile
public struct AbondProfile: View {
    static var viewDidAppearCallback: (() -> Void)?
    static var submit: (() -> Void)?

    public init(onSuccess: (() -> Void)? = nil) {
        AbondProfile.submit = onSuccess
    }

    public var body: some View {
        Text(Abond.accessToken)
            .onAppear {
                AbondProfile.viewDidAppearCallback?()
            }
    }
}

class ProfileViewTests: BaseViewControllerTests {
    private var viewController: UIViewController?

    func testSucesss() {
        let viewDidAppearExpectation = XCTestExpectation(description: "View did appear")
        AbondProfile.viewDidAppearCallback = { viewDidAppearExpectation.fulfill() }
        MockApi.mockRequest(ProfileApi.getProfileAccessToken, response: ProfileAccessToken(accessToken:"accessToken_123"))

        initialize(viewController: UIHostingController(rootView: ProfileView()))

        wait(for: [viewDidAppearExpectation], timeout: 10)
        XCTAssertEqual(Abond.accessToken, "accessToken_123")

        AbondProfile.submit!()
        // etc.
    }
}

我知道 static 變量會使測試變得脆弱——但除此之外,我很想知道是否有其他理由不這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM