Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test a function/computed property that needs device specific information?

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

like image 618
Declan McKenna Avatar asked Feb 13 '18 12:02

Declan McKenna


1 Answers

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.

like image 170
tpeodndyy Avatar answered Nov 06 '22 20:11

tpeodndyy