Trik SwiftUI yang sederhana namun kuat

Untuk memulai pengujian SwiftUI dengan baik, menggunakan kerangka ViewInspector, Anda dapat membaca ini atau ini.

Salah satu item yang dijelaskan dalam tutorial pertama (dan di halaman ViewInspector GitHub ) adalah penggunaan @State dalam tampilan yang ingin Anda uji.

Pada dasarnya, jika pandangan Anda seperti ini:

struct ContentView: View {
   @State var numClicks:Int = 0
 
   var body: some View {
      VStack{
         Button("Click me"){
            numClicks += 1
         }.id("Button1")
         Text("\(numClicks)")
           .id("Text1")
           .padding()
   
      }
   }
}

Sebenarnya tidak mungkin untuk menguji tindakan mengklik tombol pada numClicks.
Solusi yang dapat diterima adalah dengan menambahkan sedikit kode ke tampilan, mengubahnya pada dasarnya menjadi :

struct ContentView: View {
   @State var numClicks:Int = 0
   internal let inspection = Inspection<Self>()
 
   var body: some View {
      VStack{
         Button("Click me"){
            numClicks += 1
         }.id("Button1")
         Text("\(numClicks)")
           .id("Text1")
           .padding()
   
       }.onReceive(inspection.notice) { 
            self.inspection.visit(self, $0) }
    }
}

dimana Inspeksi adalah:

internal final class Inspection<V> {
   let notice = PassthroughSubject<UInt, Never>()
   var callbacks: [UInt: (V) -> Void] = [:]
   func visit(_ view: V, _ line: UInt) {
      if let callback = callbacks.removeValue(forKey: line) {
         callback(view)
      }
   }
}
extension Inspection: InspectionEmissary {}

Seperti yang Anda lihat, ContentView mendapat properti Inspeksi baru dan onReceive tubuhnya diinstruksikan untuk menjalankan metode kunjungan properti Inspeksi ketika data dipublikasikan ke penerbit yang diketahui.

Ini menyelesaikan masalah, dan test case yang berfungsi akan terlihat seperti ini:

func testContentView() throws{
   let sut = ContentView()
    _ = sut.inspection.inspect { view in
       let button = try view.find(viewWithId: “Button1”).button()
       try button.tap()
       XCTAssertEqual(try view.actualView().numClicks, 1)
       let text = try view.find(viewWithId: “Text1”).text()
       let value = try text.string()
       XCTAssertEqual(value, “1”)
    }
 }

Namun, menambahkan 'fitur' pengujian ke kode produksi Anda tampaknya agak jelek. Ini dapat membuat tampilan yang rumit menjadi lebih rumit dan banyak kode berlebihan yang perlu disalin/ditempel di setiap tampilan yang ingin Anda uji.

Oleh karena itu, saya mencoba menemukan sesuatu yang lebih bersih, yang akan menambah sedikit overhead pada kode pengujian, tetapi membiarkan implementasi tampilan sebenarnya tidak tersentuh.

Pertama saya menerapkan tampilan pembungkus sederhana, yang dapat menambahkan fungsionalitas inspeksi yang dibutuhkan oleh ViewInspector:

public let TEST_WRAPPED_ID: String = “wrapped”
struct TestWrapperView<Wrapped: View> : View{
   internal let inspection = Inspection<Self>()
   var wrapped: Wrapped
 
   init( wrapped: Wrapped ){
       self.wrapped = wrapped
   }
 
   var body: some View {
      wrapped
        .id(TEST_WRAPPED_ID)
        .onReceive(inspection.notice) { 
           self.inspection.visit(self, $0) 
        }
    }
}
extension TestWrapperView: Inspectable{}

Dengan tampilan pembungkus ini, pengujian tampilan dapat dilakukan, menggunakan implementasi asli ContentView:

func testContentView() throws{
   let sut = TestWrapperView(wrapped: ContentView())
   _ = sut.inspection.inspect { view in
       let wrapped = try view.find(viewWithId: TEST_WRAPPED_ID)
       let button = try wrapped.find(viewWithId: “Button1”).button()
       try button.tap()
       let numClicks = try wrapped
                         .view(ContentView.self)
                         .actualView()
                         .numClicks
       XCTAssertEqual(numClicks, 1)
       let text = try wrapped.find(viewWithId: “Text1”).text()
       let value = try text.string()
       XCTAssertEqual(value, “1”)
    }
 }

Saya harap Anda menyukai trik kecil ini, yang membuat pengujian tampilan SwiftUI sedikit lebih mudah.