I would like to use a fake SceneDelegate for unit tests so that the app does not create numerous real dependencies at launch. I used to create a fake AppDelegate and it worked great.
The problem is that application(_:configurationForConnecting:options:) is only called after first app launch.
It is unexpected that the function is not called on every launch. I read the documentation and did not find anything describing when this function is and is not called:
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/3197905-application
Because this function is only called during first app launch I have a problem when I run unit tests. The intention is to use TestAppDelegate and TestSceneDelegate when running unit tests so that my tests don't execute a bunch of real app launch code and use dependencies that are not mocked. This is a pretty common and good approach it seems:
If I run unit tests AFTER I run the app the TestAppDelegate is used but because the function is not called SceneDelegate is used instead of TestSceneDelegate. It's like the system caches the UISceneConfiguration.
This is bad because unintended production code is executed when I run tests.
I can work around the problem by deleting the app before I run unit tests.
It is interesting that if I run the app after running unit tests the function does get called again and so there is no problem reversing the steps.
Really hoping there is a way to specify UISceneConfiguration for each app launch because this is the cleanest way to setup for testing.
Thanks!
The problem is that application(_:configurationForConnecting:options:) is only called after first app launch.
It is unexpected that the function is not called on every launch. I read the documentation and did not find anything describing when this function is and is not called:
https://developer.apple.com/documentation/uikit/uiapplicationdelegate/3197905-application
Because this function is only called during first app launch I have a problem when I run unit tests. The intention is to use TestAppDelegate and TestSceneDelegate when running unit tests so that my tests don't execute a bunch of real app launch code and use dependencies that are not mocked. This is a pretty common and good approach it seems:
If I run unit tests AFTER I run the app the TestAppDelegate is used but because the function is not called SceneDelegate is used instead of TestSceneDelegate. It's like the system caches the UISceneConfiguration.
This is bad because unintended production code is executed when I run tests.
I can work around the problem by deleting the app before I run unit tests.
It is interesting that if I run the app after running unit tests the function does get called again and so there is no problem reversing the steps.
Really hoping there is a way to specify UISceneConfiguration for each app launch because this is the cleanest way to setup for testing.
Thanks!
Hi subclass,
I'm familiar with the pattern you're using to swap out your App Delegate for unit testing. I'm wondering if you've considered using framework-hosted tests, as opposed to app-hosted tests, so your app and scene delegates wouldn't be used at all. There's also a speed improvement to your tests if you take this approach since it won't install your app into a simulator.
Assuming you can't make your tests framework-hosted rather than app-hosted (Which you can enable by changing which host application your test target uses in its General settings), I'll admit I'm not sure if you can configure your scene delegate the way you'd like based on the caching behavior you seem to be noticing, though I have 2 ideas:
1) Make your TestAppDelegate and real AppDelegate conform to both the UIApplicationDelegate and UISceneDelegate protocol, and then use composition to forward the calls into separate implementation classes (so as to maintain the separation of concerns that's ideal with the SceneDelegate and AppDelegate classes we generate for you). I haven't tested this but it seems like, if you made both your AppDelegate and TestAppDelegate handle both protocol conformances, this would work to make sure your test double gets loaded consistently.
2) Alternatively, add a switch on the launch arguments of your iOS app inside the concrete SceneDelegate class to control the path it takes when testing, perhaps via a --unit-testing flag, so you can avoid expensive, stateful, or tricky logic you'd like to avoid for testing. You can again use composition and delegation here to keep the SceneDelegate code manageable. If you're concerned about compiling in test hooks into your app code, you can also conditionally compile this code out for release builds.
I'm familiar with the pattern you're using to swap out your App Delegate for unit testing. I'm wondering if you've considered using framework-hosted tests, as opposed to app-hosted tests, so your app and scene delegates wouldn't be used at all. There's also a speed improvement to your tests if you take this approach since it won't install your app into a simulator.
Assuming you can't make your tests framework-hosted rather than app-hosted (Which you can enable by changing which host application your test target uses in its General settings), I'll admit I'm not sure if you can configure your scene delegate the way you'd like based on the caching behavior you seem to be noticing, though I have 2 ideas:
1) Make your TestAppDelegate and real AppDelegate conform to both the UIApplicationDelegate and UISceneDelegate protocol, and then use composition to forward the calls into separate implementation classes (so as to maintain the separation of concerns that's ideal with the SceneDelegate and AppDelegate classes we generate for you). I haven't tested this but it seems like, if you made both your AppDelegate and TestAppDelegate handle both protocol conformances, this would work to make sure your test double gets loaded consistently.
2) Alternatively, add a switch on the launch arguments of your iOS app inside the concrete SceneDelegate class to control the path it takes when testing, perhaps via a --unit-testing flag, so you can avoid expensive, stateful, or tricky logic you'd like to avoid for testing. You can again use composition and delegation here to keep the SceneDelegate code manageable. If you're concerned about compiling in test hooks into your app code, you can also conditionally compile this code out for release builds.