I’m trying to unit test my code that uses an access-controlled part of the iOS/macOS system (SFSpeechRecognizer
in this case, but this would apply to any of Apple’s restricted frameworks).
I’ve setup a Host Application target with the right entitlements and Info.plist
key set. The first time I ran the unit tests in Xcode, I got the user permission prompt interface (which I clicked accept for).
Subsequent runs (even after a “Clean Build Folder…”) do not request the prompt. (Because the permission is remembered by the OS, as shown by the presence of my host app in the list of authorized apps found in System Settings → Privacy & Security → Speech Recognition.)
So, testing “the happy path” works on my local system. But I also want to test the “unhappy paths”/edge-cases, as well as be able to run the tests “headless” (CI / cloud build).
Questions:
-
Is there a way to test/mock the various authorization states (denied, restricted, notDetermined, authorized) so I can test how my functions behave under those circumstances? I suppose, in this case, having the “authorized” state confirmed, but passing in a mocked
SFSpeechRecognizerAuthorizationStatus
in place of therequestAuthorization()
call result, might work. Any recommendations? -
Is it possible to set this up so it can run on a CI/cloud-build system, where it won’t be possible to “click the authorize button UI”?
-
Is there a way to have unit tests (XCTest running in Xcode) remove a previously given authorization, so I can have it go through the authorization step again?
Any other recommendations around testing Apple’s user-authorization-controlled systems?
The thing you need to test is your code. You cannot / should not unit test how Apple behaves.
So, yes, you should mock the results from Apple’s permission calls. This may mean refactoring so that your permission request calls are wrapped in your own object where you can mock the result.
I 100% get and agree with the point that I am testing “my code”. But I need to verify that my code is interacting correctly with Apple’s (sometimes awkward, suboptimally documented, and subject to change) frameworks. So I’m not going to just stub/mock/inject all Apple dependencies and leave it at that. Taking that principle to its fullest, I could make a separate set of tests that just test my expectations for Apple’s Frameworks to verify that they continue to behave as expected, but I still need to test them in that case.
“Don’t test Apple’s code” doesn’t answer the question of how to test those behaviours using XCTest in Xcode and in CI/Cloud. Sure, they’re not “technically” just unit tests then, but actually integration tests. But the point remains: I’m looking to test those systems as part of my automated testing.