Home > Software design >  Test different behaviours in AppDelegate for each or separated unit and integration tests
Test different behaviours in AppDelegate for each or separated unit and integration tests

Time:11-12

I want to test my application's behavior that decided on app launch. For example: In a tab bar controller, how many and which tabs will be created is been decided on app launch where the root window has been created so I want to test these behaviors for each test case.

This new feature is set via A/B service and the value retrieved only during app launching. Based on that value, the tab bar's view controllers are set.

For example:

var viewControllers: [UIViewController] = [ tabOne, tabTwo]
if Config.isNewFeatureEnabled {
    viewControllers.append(self._menuCoordinator.rootViewController)
} else {
    viewControllers.append(self._anotherTabBarController)
    viewControllers.append(self._anotherCoordinator.rootViewController)
    viewControllers.append(self._someOtherCoordinator.rootViewController)
}
_tabBarController.viewControllers = viewControllers

Let me put in code, in order to make tests easy I created a protocol (not necessarily but better approach for injection)

protocol FeatureFlag {
    var isNewFeatureEnabled: Bool { get set }
}

// Implementation
class FeatureFlagService: FeatureFlag {
   var isNewFeatureEnabled = false
   // Bunch of other feature flags
}

In my test cases I want to switch the config with out effecting other side of the app. Something like this:

class NewFeatureVisibilityTests: XCTestCase {
    func test_TabBar_has_threeTabs_when_NewFeature_isEnabled() {
        // Looking for a way to inject the config

        let tabBar = getKeyWindow()?.rootViewController as? UITabBarController

        guard let tabBar = appDel.currentWindow?.rootViewController as? UITabBarController else {
            return XCTFail("Expected root view controller to be a tab bar controller")
        }

        XCTAssertEqual(tabBar.viewControllers?.count, 3)
    }

    func test_TabBar_has_fiveTabs_when_NewFeature_isDisabled() {
        // Looking for a way to inject the config

        let tabBar = getKeyWindow()?.rootViewController as? UITabBarController

        guard let tabBar = appDel.currentWindow?.rootViewController as? UITabBarController else {
            return XCTFail("Expected root view controller to be a tab bar controller")
        }

        XCTAssertEqual(tabBar.viewControllers?.count, 5)
    }
}

What I want is set application's behaviour through injection (a config etc) for each test case.

One test the feature will be enabled, other test will assert the feature disabled state.

CodePudding user response:

Create a config property in AppDelegate using existing type of FeatureFlag along with a default value on override init.

extension UIApplication {
    var currentWindow: UIWindow {
        return (connectedScenes
            .filter({$0.activationState == .foregroundActive})
            .compactMap({$0 as? UIWindowScene})
            .first?.windows
            .filter({$0.isKeyWindow}).first!)!
    }
}

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    let config: FeatureFlag!
    
    override init() {
        config = FeatureFlagService()
    }
    
    init(config: FeatureFlag!) {
        self.config = config
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        // Create a tabBar with 3 tabs
        let tabBarController = UITabBarController()
        let firstViewController = UIViewController()
        let secondViewController = UIViewController()
        let thirdViewController = UIViewController()
        let fourthViewController = UIViewController()
        let fifthViewController = UIViewController()
        
        firstViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .favorites, tag: 0)
        secondViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 1)
        thirdViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .more, tag: 2)
        fourthViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 3)
        fifthViewController.tabBarItem = UITabBarItem(tabBarSystemItem: .contacts, tag: 4)
        
        var viewControllers = [firstViewController, secondViewController]
        if config.isNewFeatureEnabled {
            viewControllers.append(thirdViewController)
        } else {
            viewControllers.append(fourthViewController)
            viewControllers.append(fifthViewController)
        }
        
        tabBarController.viewControllers = viewControllers
        
        // Create a window and set the root view controller
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = tabBarController
        window.makeKeyAndVisible()
        self.window = window
        
        return true
    }
}

And in tests, I set my config, create an instance of AppDelegate, inject the config, and launching the application through appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil) function of AppDelegate.

let appDelegate = AppDelegate(config: config)

        // This is the key function
        _ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)

Tests:

import XCTest
@testable import ExampleApp


final class NewFeatureVisibilityTests: XCTestCase {
   
    func test_app_can_start_with_isNewFeatureEnabled(){
        let config = FeatureFlagService()
        config.isNewFeatureEnabled = true
        let appDelegate = AppDelegate(config: config)

        // This is the key function
        _ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
        
        guard let rootVC = UIApplication.shared.currentWindow.rootViewController as? UITabBarController else {
            return XCTFail("RootViewController is nil")
        }
        
        XCTAssertEqual(rootVC.viewControllers?.count, 3)
    }
    
    func test_app_can_start_with_isNewFeatureDisabled(){
        let config = FeatureFlagService()
        config.isNewFeatureEnabled = false
        let appDelegate = AppDelegate(config: config)

        // This is the key function
        _ = appDelegate.application(UIApplication.shared, didFinishLaunchingWithOptions: nil)
        
        guard let rootVC = UIApplication.shared.currentWindow.rootViewController as? UITabBarController else {
            return XCTFail("RootViewController is nil")
        }
        
        XCTAssertEqual(rootVC.viewControllers?.count, 4)
    }
}

  • Related