What's the best way to test instance types

So in my younger years of using SwiftData (last year) I had a rough road to correctly set up Schema migrations.

After much trial and error I was able to set up an automated test per migration stage, which in essence tests if executing the actual migration crashes my app or not.

I want to make sure the data model instances are certain to be

  • of the previous Schema before the migration and
  • of the next Schema after the migration

However, Swift testing tells me that 'is' test is always true which I find hard to believe to be true...

Is there a better way to test if an instance is of a certain class in Swift Testing?

Maybe the compiler is right. Maybe you are testing the framework, which is not always useful.

Regardless:

Is there a better way to test if an instance is of a certain class in Swift Testing?

I don't know if it helps, but I sometimes write tests that don't fail at runtime, but at compile time. For example, here is how I would test that computation(input:) returns a String, without running it:

// This test passes if it compiles
@Test func computation_returns_string() {
  func test(input: SomeInput) {
    let result: String = computation(input: input)
  }
}
1 Like

This doesn't look like it's a question about Swift Testing. is is a feature of the Swift language, not Swift Testing. :slightly_smiling_face:

2 Likes

What I want to test is that the code I wrote in my SwiftData migration doesn't crash the app. Really small things, from the way you define the Codable implementation from one version to another for example, while otherwise fine, for SwiftData can crash migrations at runtime.

I hope that makes sense to test.

I sometimes write tests that don't fail at runtime, but at compile time.

SwiftData migrations crash at runtime sadly.

The question is about the best way to test instances in Swift Testing. : ) I hope that makes sense.

1 Like

Yes, probably an app that uses SwiftData needs tests ;-) Sorry I could not help more. Maybe in the future you'll give GRDB a try (that's a mature persistence library that makes its users happy).

2 Likes

I also do this, and hate that it's not standardized. You can write

_ = computation(input: input) as String

but nobody knows why you did it without a comment. I think something like the following ought to be in Swift Testing.

func requireCompilation(_: some Any) { }
requireCompilation(computation(input: input) as String)

Swift compiler here is right: it operates on the type system and your FetchDescriptor specify exactly that type, so yes — this check is always true from that standpoint. It also gives a hint that you might not be testing what you think you are testing.

The deal is that SwiftData tries to map underlying data to the type you are expecting. Consequently, if it fails — it crashes. I would look up what Apple says in docs and on WWDC about testing that.

I have no experience with SwiftData, but I can absolutely certainly say that checks via is won’t fly because they work at the language type system level, and have no relation to framework in that case. More likely you need less strict setup in terms of type (maybe it is possible to fallback to CoreData in tests, for example), it might be proved more difficult to achieve.

Note also that SwiftData is name of Apple’s framework and is a black box, it is not part of the Swift language.

3 Likes

I see, so from what I gather here, I believe the anwser is that my test might be valid in the sense that it can test if the migration will succeed or crash, but the extra check I do to test if the fetched records are really instances of the type before / after the migration is pretty much useless because it will always be that case.

So I can safely delete the instance is class checks and keep the rest. : )

Thanks for teaching me new things!

1 Like