เคล็ดลับ SwiftUI ที่เรียบง่ายแต่ทรงพลัง
เพื่อการเริ่มต้นการทดสอบ SwiftUI ที่ดีโดยใช้ ViewInspector
framework คุณสามารถอ่าน "this" หรือ "this" ได้
หนึ่งในรายการที่อธิบายไว้ในบทช่วยสอนแรก (และบนหน้า ViewInspector GitHub ) คือการใช้ @State
ในมุมมองที่คุณต้องการทดสอบ
โดยพื้นฐานแล้ว หากความคิดเห็นของคุณเป็นดังนี้:
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() } } }
จริงๆ แล้วเป็นไปไม่ได้ที่จะทดสอบการทำงานของการคลิกปุ่มบน numClicks
วิธีแก้ไขเบื้องต้นที่ยอมรับได้คือการเพิ่มโค้ดเล็กน้อยลงในมุมมอง โดยเปลี่ยนโดยทั่วไปเป็น :
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) } } }
โดยที่การตรวจสอบคือ:
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 {}
อย่างที่คุณเห็น ContentView
ได้รับคุณสมบัติการตรวจสอบใหม่และ onReceive ของเนื้อหาได้รับคำสั่งให้เรียกใช้วิธีการเยี่ยมชมของคุณสมบัติการตรวจสอบเมื่อมีการเผยแพร่ข้อมูลไปยังผู้เผยแพร่ที่สังเกตเห็น
วิธีนี้ช่วยแก้ปัญหาได้ และกรณีทดสอบการทำงานอาจมีลักษณะดังนี้:
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”) } }
อย่างไรก็ตาม การเพิ่ม 'คุณสมบัติ' การทดสอบลงในโค้ดที่ใช้งานจริงของคุณดูน่าเกลียด มันสามารถทำให้มุมมองที่ซับซ้อนซับซ้อนยิ่งขึ้น และมีโค้ดซ้ำซ้อนจำนวนมากที่ต้องคัดลอก/วางในแต่ละมุมมองที่คุณต้องการทดสอบ
ดังนั้นฉันจึงพยายามค้นหาสิ่งที่สะอาดกว่า ซึ่งจะเพิ่มค่าใช้จ่ายเล็กน้อยให้กับโค้ดการทดสอบ แต่ปล่อยให้การใช้งานมุมมองจริงไม่ถูกแตะต้อง
ก่อนอื่น ฉันใช้งานมุมมอง wrapper แบบธรรมดา ซึ่งสามารถเพิ่มฟังก์ชันการตรวจสอบที่จำเป็นสำหรับ 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{}
ด้วยมุมมองแบบ wrapper นี้ การทดสอบมุมมองจึงเป็นไปได้ โดยใช้การใช้งานดั้งเดิมของ 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”) } }
ฉันหวังว่าคุณจะชอบเคล็ดลับเล็กๆ น้อยๆ นี้ ซึ่งจะทำให้การทดสอบมุมมอง SwiftUI ง่ายขึ้นเล็กน้อย