Abstraction through protocols makes UI testing easy

In some of the recent updates of Rigelian there have been bugs that really should have been caught before releasing, but slipped through because of a lack of automated tests. Therefor I revisited the test strategy.

In the past I created some ui-tests, but they were relying on a local mpd player that was started before running the test-cases and shutdown again afterwards. The player was configured with some standard albums. This worked locally but broke when I switched to a new computer, and the approach is also not suited if the application is ever moved into a ci/cd platform like Bitrise.

Luckily the architectural design of the app made a different approach easy: I created a specific test-implementation of the ConnectorProtocol which abstracts an actual network player like mpd or kodi from the app. This test connector mimics a player by implementing the control, browse and status protocols, thereby removing all external dependencies.

The following code snippet from the AppDelegate class will return the player browsers that take care of discovery of players. When run normally, the mpd and kodi browsers are activated, but when testing instead the in-memory test player browser is returned. It will immediately ‘discover’ and activate the test player which is then available for all test activities.

import TestConnector

func getPlayerBrowsers() -> [PlayerBrowserProtocol] {
  #if DEBUG
  if CommandLine.arguments.contains("-test") {
    return [TestPlayerBrowser.init()]
  }
  #endif
         
  return [MPDPlayerBrowser.init(),
          KodiPlayerBrowser.init()]
}

This is a nice example of how good abstraction can make testing easier.