swift-fakes icon indicating copy to clipboard operation
swift-fakes copied to clipboard

Faking out properties is a bit of a handful.

Open younata opened this issue 2 months ago • 0 comments

If you try to apply a spy to a property, you're gonna have a bad time.

Read-only properties are at least not terrible, because they behave like standard functions.

final class FakeThing: Thing {
    var propertySpy = Spy<Void, Int>(1337)
    var property: Int { propertyFake() }
}

However, settable properties are just a terrible user experience. As of this issue, the only options are to either have the fake update spy's stub, or create 2 spies for the single property.

final class FakeThing: Thing {
    var updatingSpy = Spy<Void, Int>(1337)
    var updating: Int {
        get { propertyFake() }
        set { propertyFake.stub(newValue) }
    }

    var settableReadSpy = Spy<Void, Int>(1234)
    var settableWriteSpy = Spy<Int, Void>()
    var settable: Int {
        get { settableReadSpy() }
        set { settableWriteSpy(newValue) }
    }
}

Either one is not great. Spy.stub(_:) is only supposed to be called in a test, not in the implementation of a Fake. However, re-stubbing the spy in the fake does make the Fake behave a little more "real" - I would expect SomeRandomValue.property to return the same value I just set, which this single-spy approach does handle nicely. Furthermore, the double-spy approach is just an unwieldy mouthful, even if it is more able to catch assignments.

So, I think a PropertySpy would be an excellent thing to create, which essentially is 2 spies, for tracking reads as well as writes, with calls to the write updating the stub of the read (and an option to disable that behavior).

Additionally, setting all this up is a lot of boilerplate that you shouldn't have to engage in. Thankfully, swift offers ways around this, such as property wrappers and attached macros. Either one should work, with property wrappers being significantly easier to maintain.

younata avatar Mar 31 '24 02:03 younata