1

I'm trying to learn how to use MVVM architecture with TDD to solve some of the problems with not being able to unit test Views in SwiftUI.

I have an Alarm struct which takes a date:

import Foundation

struct Alarm {
    var time: Date
}

And I have a basic

class AlarmPickerViewModel: ObservableObject {
    @Published var alarm: Alarm

    init(alarm: Alarm) {
        self.alarm = alarm
    }

}

I'm struggling to work out how to write a unit test that fails if the AlarmPickerViewModel isn't a subclass of ObservableObject and the alarm property isn't @Published.

I've looked at this question on the site but it doesn't seem to help me.

Any pointers on where I'm going wrong please?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Kramer
  • 338
  • 2
  • 15
  • 1
    "some of the problems with not being able to unit test Views in SwiftUI." That is not true. In SwiftUI you can unit test views - or at least business logic in views, since you can use dependency injection the same way as you would for any other entities. The problem of `UIKit` `UIViews` and `UIViewController`s not being dependency injectable has been resolved by SwiftUI. The only part that you cannot unit test is the actual look of your UI. – Dávid Pásztor Jun 02 '20 at 15:03
  • Thanks, that's really useful to know. – Kramer Jun 02 '20 at 15:46

1 Answers1

3

You can create a test that won't even compile if alarm is not @Published by simply creating a subscription to that property, since you will only be able to subscribe to it if it is @Published.

The ObservableObject conformance adds an objectWillChange Publisher to your object, so to test that, you simply need to subscribe to that Publisher. If AlarmPickerViewModel wasn't ObservableObject, the test won't even compile.

func testAlarmPickerViewModel() {
    let alarmPickerViewModel = AlarmPickerViewModel(alarm: Alarm(time: .distantFuture))

    alarmPickerViewModel.$alarm.sink(receiveValue: { print("ViewModel.alarm updated, new value: \($0)") })

    alarmPickerViewModel.objectWillChange.sink(receiveValue: { print("ViewModel updated: \($0)")})
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116