Writing units tests for the view models of the WeekViewController class is just as easy as writing unit tests for the DayViewModel struct. We start with the unit tests for the WeekViewModel struct.
Unit Testing the Week View Model
Create a new XCTestCase subclass and name it WeekViewModelTests.swift.

The approach we take in this episode is identical to the approach we took in the previous episode. Add an import statement for the Cloudy module and define a property for the view model. The viewModel property is an implicitly unwrapped optional.
WeekViewModelTests.swift
import XCTest
@testable import Cloudy
class WeekViewModelTests: XCTestCase {
// MARK: - Properties
var viewModel: WeekViewModel!
// MARK: - Set Up & Tear Down
override func setUpWithError() throws {
}
override func tearDownWithError() throws {
}
}
In the setUpWithError() method, we load the stub from the unit testing bundle and convert it to a WeatherData object. We use the value of the dailyData property to create a WeekViewModel object. Remember that the dailyData property is of type [WeatherDayData].
WeekViewModelTests.swift
override func setUpWithError() throws {
// Load Stub
let data = loadStub(name: "weather", extension: "json")
// Create JSON Decoder
let decoder = JSONDecoder()
// Configure JSON Decoder
decoder.dateDecodingStrategy = .secondsSince1970
// Decode JSON
let weatherData = try decoder.decode(WeatherData.self, from: data)
// Initialize View Model
viewModel = WeekViewModel(weatherData: weatherData.dailyData)
}
The unit tests for the WeekViewModel struct are easy to write. The simplest unit test of this series is the one for the numberOfSections computed property. Remember that numberOfSections always returns 1.
WeekViewModelTests.swift
// MARK: - Tests for Number of Sections
func testNumberOfSections() {
XCTAssertEqual(viewModel.numberOfSections, 1)
}
The unit test for numberOfDays is just as easy to implement. A single assertion is sufficient.
WeekViewModelTests.swift
// MARK: - Tests for Number of Days
func testNumberOfDays() {
XCTAssertEqual(viewModel.numberOfDays, 8)
}
Unit testing the viewModel(for:) method is slightly more complicated. We can take a few approaches. Remember that the viewModel(for:) method returns an object that conforms to the WeatherDayPresentable protocol. One approach is to ask the view model for the object that corresponds with a predefined index and assert that the day and date properties are equal to the values we expect based on the stub included in the unit testing bundle.
WeekViewModelTests.swift
// MARK: - Tests for View Model for Index
func testViewModelForIndex() {
let weatherDayViewModel = viewModel.viewModel(for: 5)
XCTAssertEqual(weatherDayViewModel.day, "Saturday")
XCTAssertEqual(weatherDayViewModel.date, "June 27")
}
These are the unit tests we need to write for the WeekViewModel struct. Press Command + U to run the test suite.

Unit Testing the Weather Day View Model
You should now be able to write the unit tests for the WeatherDayViewModel struct. The unit tests are similar to those of the DayViewModel struct. The only difficulty is creating the view model. Give it a try to see if you can make it work. You can find the solution in the finished project of this episode.
We create a new XCTestCase subclass and name it WeatherDayViewModelTests.swift.

We add an import statement for the Cloudy module and define a property with name viewModel. The viewModel property is an implicitly unwrapped optional.
WeatherDayViewModelTests.swift
import XCTest
@testable import Cloudy
class WeatherDayViewModelTests: XCTestCase {
// MARK: - Properties
var viewModel: WeatherDayViewModel!
// MARK: - Set Up & Tear Down
override func setUpWithError() throws {
}
override func tearDownWithError() throws {
}
}
We create the view model in the setUpWithError() method. We load the stub from the unit testing bundle and convert it to a WeatherData object. We use the WeatherData object to create the WeatherDayViewModel object. Because we need a WeatherDayData object to create the WeatherDayViewModel object, we ask the WeatherData object for a WeatherDayData object. That is the only complexity of the unit tests for the WeatherDayViewModel struct.
WeatherDayViewModelTests.swift
override func setUpWithError() throws {
// Load Stub
let data = loadStub(name: "weather", extension: "json")
// Create JSON Decoder
let decoder = JSONDecoder()
// Configure JSON Decoder
decoder.dateDecodingStrategy = .secondsSince1970
// Decode JSON
let weatherData = try decoder.decode(WeatherData.self, from: data)
// Initialize View Model
viewModel = WeatherDayViewModel(weatherDayData: weatherData.dailyData[5])
}
The unit tests should look familiar. They are similar to the ones we wrote for the DayViewModel struct.
WeatherDayViewModelTests.swift
// MARK: - Tests for Day
func testDay() {
XCTAssertEqual(viewModel.day, "Saturday")
}
// MARK: - Tests for Date
func testDate() {
XCTAssertEqual(viewModel.date, "June 27")
}
// MARK: - Tests for Temperature
func testTemperature_Fahrenheit() {
let temperatureNotation: TemperatureNotation = .fahrenheit
UserDefaults.standard.set(temperatureNotation.rawValue, forKey: "temperatureNotation")
XCTAssertEqual(viewModel.temperature, "65 °F - 83 °F")
}
func testTemperature_Celsius() {
let temperatureNotation: TemperatureNotation = .celsius
UserDefaults.standard.set(temperatureNotation.rawValue, forKey: "temperatureNotation")
XCTAssertEqual(viewModel.temperature, "18 °C - 28 °C")
}
// MARK: - Tests for Wind Speed
func testWindSpeed_Imperial() {
let unitsNotation: UnitsNotation = .imperial
UserDefaults.standard.set(unitsNotation.rawValue, forKey: "unitsNotation")
XCTAssertEqual(viewModel.windSpeed, "6 MPH")
}
func testWindSpeed_Metric() {
let unitsNotation: UnitsNotation = .metric
UserDefaults.standard.set(unitsNotation.rawValue, forKey: "unitsNotation")
XCTAssertEqual(viewModel.windSpeed, "10 KPH")
}
// MARK: - Tests for Image
func testImage() {
let viewModelImage = viewModel.image
let imageDataViewModel = viewModelImage!.pngData()!
let imageDataReference = UIImage(named: "clear-day")!.pngData()!
XCTAssertNotNil(viewModelImage)
XCTAssertEqual(viewModelImage!.size.width, 236.0)
XCTAssertEqual(viewModelImage!.size.height, 236.0)
XCTAssertEqual(imageDataViewModel, imageDataReference)
}
In the tearDownWithError() method, we reset the state we set in the unit tests.
WeatherDayViewModelTests.swift
override func tearDownWithError() throws {
// Reset User Defaults
UserDefaults.standard.removeObject(forKey: "unitsNotation")
UserDefaults.standard.removeObject(forKey: "temperatureNotation")
}
The view models we created in the previous episodes are now fully covered by unit tests. Run the test suite one more time to make sure the unit tests pass.

What's Next?
In the second part of this series, we take the Model-View-ViewModel pattern to the next level by introducing bindings, an essential component of the Model-View-ViewModel pattern.