8

I'm developing a calendar app where I'm struggling to create tests for the functions that utilize the calendars on the users device. The calendar: EKCalendar variable is taken from the users event store so when the unit tests run on XCode's emulator they fail as the calendar doesn't exist. Using my personal device works but then the tests would fail on our build server.

What are good approaches to test a function/computed property that uses the User's calendar/eventstore?

    ///Return the user's name. If this hasn't been saved extract their name from their Enterprise calendar and save it.
static var name: String? {
    if let savedName = defaults.string(forKey: "name") {
        return savedName
    }

    // calendar is a specific EKCalendar members of my company will have.
    guard let calendar = MeetingsFetcher().getUserEnterpriseCalendars().first,
        let parsedName = calendar.title.firstMatch(from: Regex.organizerNameFromEKCalendar) else {
            return nil
    }
    defaults.set(parsedName, forKey: "name")
    return parsedName
}


func getEnterpriseCalendars() -> [EKCalendar]{
    guard EKEventStore.authorizationStatus(for: .event) == .authorized else { return [EKCalendar]() }
    for calendar in MeetingsFetcher.eventStoreClass.calendars(for: .event) {
        if calendar.source.title.range(of: "IBM") != nil{
            return [calendar]
        }
    }
    return [EKCalendar]()
}

Ignore that it is returning an array, I'm not really sure why it does that :p

Declan McKenna
  • 4,321
  • 6
  • 54
  • 72
  • 4
    Usually for this purposes you may use a Mock object which simulates behavior of your real device. In order to have it working only for Simulator you may use one of subjection from here: https://stackoverflow.com/questions/24869481/detect-if-app-is-being-built-for-device-or-simulator-in-swift – Dmitry A. Feb 13 '18 at 13:07

1 Answers1

3

So let clarify what you actually have to test in your function.

  1. MeetingsFetcher().getUserEnterpriseCalendar()

  2. calendar.title.firstMatch(from: Regex.organizerNameFromEKCalendar)

My assumption is your want to test both of them. But as the issue you mention I think it's impossible to actually test if your getUserEnterpriseCalendar function is actually work correctly on simulator because it won't return anything anywhere (except you create the script on your build server to let simulator subscribe or add your calendar on it)

However I can suggest you to mock getUserEnterpriseCalendar function and make an assumption in your unit test that getUserEnterpriseCalendar will return correct value in any environment. You may need to chnage your computed property to function and pass MeetingFetcher as parameter for this.

// function modification
static func name(from meetingFetcher: MeetingFetcher) -> String {
    if let savedName = defaults.string(forKey: "name") {
        return savedName
    }

    // calendar is a specific EKCalendar members of my company will have.
    guard let calendar = meetingFetcher.getUserEnterpriseCalendar().first,
        let parsedName = calendar.title.firstMatch(from: Regex.organizerNameFromEKCalendar) else {
            return nil
    }
    defaults.set(parsedName, forKey: "name")
    return parsedName
}

// In testing code.
func testOrganizationCalendar() {

    class MockedMeetingFetcher: MeetingFetcher {
        override func getUserEnterpriseCalendars() -> [EKCalendar] {
            let calendar1 = EKCalendar()
            calendar1.title = "ExpectedToMatchRegexTitle"
            return [calendar1]
        }
    }

    XCTestAssertEqual(YourClass.name(from: MockedMeetingFetcher()), "Hure!!!")

}

I hope it may help even just give you some ideas.

tpeodndyy
  • 369
  • 2
  • 3
  • I'm mostly looking to test if the EKEventStore has calendars within it. It turns out `EKEventStore.calendars` is always empty if you ever attempt to access the event store prior to obtaining the users permission. A unit test to alert me when this has happened would be nice to prevent this happening in future but by the sounds of things isn't possible. Have you ever heard of anyone programatically inserting calendars in to their build server's emulator? I suspect apple wouldn't allow this. – Declan McKenna Mar 10 '18 at 19:37
  • It's possible to perform deeplink into Calendar app (using `webcal://` scheme) to subscribe or add calendar. However you still need to find a way to execute "Subscribe" button when calendar app is opened which is possible. – tpeodndyy Mar 11 '18 at 08:23