This workshop will be retired on May 31, 2020.
Testing your ViewModel11:15 with Amit Bijlani
Since the ViewModel is a value type we can take advantage of it and add unit testing to our app. The unit tests assert values to confirm that we are getting valid outputs.
So let's talk about unit testing. 0:00 Unit testing is important because if the ViewModel displays data based on certain 0:02 business rules, then we can test those assumptions against edge cases. 0:07 In our example, the properties within our ViewModel are simple, and 0:12 we will write unit tests for them. 0:17 We will create an instance of the CurrentWeatherViewModel with some data in 0:20 our set up method. 0:24 And since the ViewModel and the Model are structs, which means they are value types 0:26 we can simply initialize them with the same values and compare the results. 0:32 So first thing we need to do to this project is add a new target. 0:38 So heading over to the project settings, we can add target and 0:42 we'll select the iOS unit testing bundle, hit Next. 0:48 All right, so this adds the test target, and 0:53 it also creates a folder called StormyTests, and 0:58 we have this sample test file called StormyTest with the set up and 1:02 teardown methods and example test methods. 1:07 So in our setUp, method we will initialize a current weather model. 1:12 So we'll create a var, we'll say a currentWeather type CurrentWeather. 1:20 So you will notice that as I type, the auto complete does not show me this model 1:28 and the compiler complains, saying use of undeclared type CurrentWeather. 1:32 So what's going on? 1:37 Well, we created a new test target. 1:39 However, none of the file that we have created here 1:42 in our Stormy project are part of this target. 1:45 So let's go ahead and add these files to our target. 1:50 So when we click on a file, and in our utility pane, 1:55 the very first tab is the file inspector. 1:59 And you can see that the target membership does not include the test target. 2:03 So we can just go ahead and include all of these files. 2:08 We'll probably need the weather icon because it's used by 2:11 the current weather model. 2:15 So I've gone ahead and 2:17 added all of these three models into our StormyTest test target. 2:19 And now it should not complain. 2:25 Here you go. 2:27 If I did not make this an optional then the compiler would complain that we need 2:28 a designated initializer. 2:32 So, I've simply made this an optional and will go ahead and 2:35 set it up in our setUp methods. 2:38 It would say self.currentWeather = CurrentWeather. 2:41 Oops, And 2:46 we need to initialize it with certain values. 2:52 So let's give it a nice little temperature, we'll say 65 degrees. 2:56 The percentage of humidity 25%, precipitation 10%. 3:01 Summary string, "What a beautiful day". 3:07 Weather icon, we'll say weather icon. 3:14 Believe that's how it goes. 3:29 And simply format this a little so it looks nice. 3:32 All right, so we have initialized our current weather model and 3:39 in the teardown, we can simply assign a nil, we'll say nil. 3:44 Okay, and the compiler is still complaining, so why is it complaining? 3:51 It does not like this for some reason. 3:57 We forgot a closing parenthesis, right? 4:01 Okay, it's still complaining. 4:10 Cannot convert the value of type WeatherIcon to unexpected 4:12 argument type UIImage. 4:17 Right, because WeatherIcon has an image property, so we need to call that. 4:19 So going back to the WeatherIcon, I wanna make sure so, 4:29 it's all lowercase clear-day. 4:33 So let's go back here and say clear-day. 4:35 Okay, so we've set up our model which we're going to be testing against. 4:41 So here we'll see testViewModel. 4:48 And, So 4:55 we'll create a viewModel from model, so 5:02 we'll say CurrentWeatherViewModel, which takes in the currentWeather. 5:06 So we'll say CurrentWeather. 5:14 And now we can write tests against it, so 5:17 we'll say XCAsserts, NotNil. 5:21 So I wanna make sure that the ViewModel that has been instantiated is not nil. 5:31 Okay, it's complaining, so what is it saying? 5:35 It's saying the value of optional type not unwrapped. 5:38 Right, so our Current Weather property is an optional, so 5:42 we need to unwrap that optional. 5:47 So we'll create a guard let statement and 5:49 let say guard let weather = self.currentWeather. 5:52 And if it is snow, it will simply return. 5:57 So here instead of currentWeather, we will use weather. 6:00 Okay, so we need to write furthermore asserts, 6:07 so we'll say XCTAssertEqual. 6:14 So the rest of the expressions we can remove this test performance example. 6:19 So the rest of them are all AssertEquals. 6:29 If we scroll up here, you will see that there is temperature, 6:32 humidity, precipitation possibility. 6:36 So, we have to test whether the viewmodel.temperatureString 6:40 is equal to something. 6:47 So, we know how we constructed the temperatureString. 6:49 So going back here to our view model we can simply copy this here. 6:53 And we'll say, 7:02 it should really read as 65. 7:05 And that little sign that we had, this little symbol, the degree symbol. 7:10 So that's the great thing about value types. 7:18 We create this value and it simply compares that value against 7:20 the string that's been created in our view model. 7:25 So we'll create another XCTAssertEqual, 7:29 we'll say viewmodel.humidityString. 7:35 So what's the humidity? 7:40 It's 25%. 7:41 XCAssert, viewModel, 7:49 PrecipitationProbabilityString and this is 10%. 7:55 As you can see, we're testing all of the properties that we have created. 8:04 This one we can simply just copy, as is. 8:10 This is a UIImage, so simply say named. 8:23 We give it this clear-day. 8:30 All right, so all of this code should be very familiar to you. 8:35 We used the guard let statement to unwrap the optional, 8:38 we created a viewModel then we're testing if it's not nil, and 8:42 whether all of the properties are equal to the values that we give it. 8:46 So let's test our viewModel. 8:52 We got one error that says, use of undeclared type JSONDecodable. 8:55 So within our CurrentWeather viewModel we're using 9:03 this type called JSONDecodable, and 9:07 I believe this is part of the forecast client, if I'm not mistaken. 9:10 Let's go back here, and we can Cmd+click here, and it's part of the API client. 9:18 So let's add our API client to the test target, 9:24 and we'll go back to our test once again and we will run it. 9:30 So the build succeeded and now it's running the test and the test succeeded. 9:35 We get this check mark over here. 9:41 Just to make sure that this is working properly, 9:43 we're going to, let's just change the values a little bit. 9:47 And our test failed. 9:55 So it says the XCTAssertEqual failed 9:58 because 25% is not equal to 2%, 10:02 and the other two techs are not equal. 10:07 So this is great. 10:11 So this is the biggest benefit of the MVVM design pattern, its testability. 10:13 Adding new features to an app almost always results in some regression bugs. 10:20 The biggest benefit of using the MVVM design pattern is testability. 10:26 The more tests we add to our code, 10:31 the more we can prevent regression bugs from occurring. 10:32 Resulting in making our app stable as it evolves. 10:36 If you like to find out more about testing, 10:40 check out the teacher notes below. 10:42 I encourage you to look at your own app and 10:45 see how you can implement the MVVM design pattern. 10:47 Check out the teachers notes for further reading on MVVM. 10:51 Finally, I encourage you to take a look at the apps you've created. 10:55 Take an inventory of the roles and 10:59 responsibilities played by your controller. 11:01 See how you can refactor the app and apply the MVVM design pattern. 11:04 You might surprise yourself at how easy it is to implement. 11:08 So good luck, and see you next time. 11:12
You need to sign up for Treehouse in order to download course files.Sign up