ViewInspector icon indicating copy to clipboard operation
ViewInspector copied to clipboard

callOnChange Throws Errors

Open a-shatou opened this issue 3 years ago • 3 comments

I am having issue using callOnChange. It always throws "MyView does not have 'onChange' modifier". I have tried calling it on the main view (sut) and its embedded NavigationView (on which onChangeOf is actually called). What I am missing? 🤔

a-shatou avatar Mar 03 '22 20:03 a-shatou

Hey,

I just tried calling onChange for the following test view:

struct MyView: View, Inspectable {
    
    let val = "initial"
    
    var body: some View {
        NavigationView {
            Text("Hello")
        }.onChange(of: val) { value in
            
        }
    }
}

and test:

func testOnChange() throws {
    guard #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
    else { throw XCTSkip() }
    let sut = try MyView().inspect().navigationView()
    try sut.callOnChange(newValue: "123")
}

It does not throw the error. Try running it on your end, compare MyView to the view you have and let me know the essential difference.

nalexn avatar Mar 04 '22 13:03 nalexn

Thanks for the quick reply. Your code works fine. Mine doesn't. I have been trying to debug it and change your code to fail and I think I managed to break it. Here is the code after the modification:

The model:

    class ScoreObject: NSObject, ObservableObject {
        @Published var score: String? = "10"
    }
   
    class UserProgress: NSObject, ObservableObject {
        @Published var scoreObject: ScoreObject = ScoreObject()
    }

The view and its test:

    struct MyView: View, Inspectable {
        
        @ObservedObject fileprivate var val = UserProgress()
        
        var body: some View {
            NavigationView {
                Text("Hello")
            }.onChange(of: self.val.scoreObject.score) { value in
                
            }
        }
    }

    func testOnChange() throws {
        let sut = try MyView().inspect().navigationView()
        try sut.callOnChange(newValue: "20")
    }

If you run this test, it will fail with this error: caught error: "NavigationView<Text> does not have 'onChange' modifier". The difference here is that the object I am observing is an optional. If I removed the ?, the test will succeed.

This is the same as my issue. Mine is a property of an NSManagedObject. The best practice for such objects to have them all optional. It breaks the tests, though. I can look for a workaround, but it would be nice if callOnChange could detect that and save me more lines of test code.

The other solution is to have onChangeOf get called by itself. By that I mean detecting the change during the test run and call its perform block without the need to callOnChange.

a-shatou avatar Mar 06 '22 20:03 a-shatou

Found the bug and fixed it. Thank you for reporting!

Until the fix is published, a workaround is to explicitly wrap the parameter in Optional. That is, instead of

try sut.callOnChange(newValue: "123")

use

try sut.callOnChange(newValue: Optional("123"))

nalexn avatar Mar 07 '22 08:03 nalexn

Released with v0.9.2

nalexn avatar Sep 17 '22 14:09 nalexn