1
00:00:05,990 --> 00:00:10,980
Hey everybody has gone this is Caleb with Dev slopes dotcom and in this video we're going to be talking

2
00:00:11,010 --> 00:00:16,010
a lot about unit testing but specifically related to asynchronous operations.

3
00:00:16,140 --> 00:00:21,540
If you're using a sync code there is a very specific way that you have to use unit testing if you're

4
00:00:21,540 --> 00:00:23,460
going to be using it and you should.

5
00:00:23,640 --> 00:00:25,470
So let's go ahead and dive right into it.

6
00:00:25,500 --> 00:00:27,780
So I'm going to pull open a new project.

7
00:00:27,780 --> 00:00:33,640
This is different than the previous project at the beginning of this of this section.

8
00:00:33,750 --> 00:00:38,220
And basically I'm going to show you what it is I'm going to walk you through the code and then we're

9
00:00:38,220 --> 00:00:42,690
going to implement some tests and test the asynchronous operations.

10
00:00:42,930 --> 00:00:48,780
So this is a little app called Photo load and I'm going to go ahead and pull open the simulator here

11
00:00:48,780 --> 00:00:50,460
so you can see what it is.

12
00:00:50,520 --> 00:00:56,850
Basically you're presented with a table view with some placeholder images images download from online

13
00:00:56,900 --> 00:01:03,870
and then what happens is as soon as they are all 100 percent downloaded they are stored into a cache

14
00:01:03,960 --> 00:01:07,120
so that they do not you know initialize.

15
00:01:07,140 --> 00:01:11,650
And it's nice and smooth and very very clean.

16
00:01:11,700 --> 00:01:13,920
So we're going to go ahead and walk through the code.

17
00:01:13,920 --> 00:01:18,600
It's a very simple app downloads images from the internet stores them into a table view in their own

18
00:01:18,600 --> 00:01:18,990
cell.

19
00:01:18,990 --> 00:01:21,720
When the image is done downloading which is very cool.

20
00:01:21,720 --> 00:01:25,140
So let's go ahead and take a quick look at the View Controller.

21
00:01:25,200 --> 00:01:27,060
We've got a basic table view linked up.

22
00:01:27,060 --> 00:01:32,970
We've got its data source and delegate set and then we return the number of items for the number of

23
00:01:32,970 --> 00:01:36,210
images I hardcoded that in which you wouldn't normally do.

24
00:01:36,210 --> 00:01:37,940
But for this example it's ok.

25
00:01:38,280 --> 00:01:42,970
Then basically I created an instance of a cell that's of type photocell.

26
00:01:42,990 --> 00:01:44,310
We'll get into that in a second.

27
00:01:44,610 --> 00:01:49,910
And then we return it and the height for each row is 200 for the photocell.

28
00:01:49,950 --> 00:01:56,580
Basically there is an image view and there's an activity indicator view when the cell first loads the

29
00:01:56,640 --> 00:01:58,350
indicator is spinning.

30
00:01:58,530 --> 00:01:59,750
It's not hidden.

31
00:01:59,820 --> 00:02:04,710
And then there's a function that can be called upon each cell which you can see back here in the table

32
00:02:04,710 --> 00:02:08,050
view for each cell we call download image.

33
00:02:08,220 --> 00:02:13,020
And basically we're passing in an image you are l from an array that I'll show you in just a second.

34
00:02:13,230 --> 00:02:18,320
But basically what happens is we're utilizing what's called and ask cache k.

35
00:02:18,660 --> 00:02:26,490
And basically we are saying hey if there is an image already saved into the cache we're going to set

36
00:02:26,490 --> 00:02:28,670
the image view to be that image.

37
00:02:28,770 --> 00:02:30,840
But from the beginning they won't be right.

38
00:02:30,840 --> 00:02:32,640
They have to be downloaded first.

39
00:02:32,670 --> 00:02:37,280
So if that's not the case it's going to go ahead and continue to the u r l session.

40
00:02:37,290 --> 00:02:43,410
So what we're doing is we're passing in the U R L we get a completion handler with data a response and

41
00:02:43,500 --> 00:02:45,750
an error which all three are optional.

42
00:02:45,750 --> 00:02:50,270
If the error is not nil we're going to go ahead and print the error and then return.

43
00:02:50,280 --> 00:02:56,760
Now granted we could you know call fatal error and it would do the same thing it would crash it would

44
00:02:56,760 --> 00:03:00,250
print what's wrong but I'm assuming that there's not an error.

45
00:03:00,300 --> 00:03:07,740
We're going to go ahead and put this on the main queue using dispatch queue and our sorry the main thread

46
00:03:08,250 --> 00:03:14,910
and we're basically going to take the data that comes from the session response or sorry from the URL

47
00:03:16,410 --> 00:03:18,450
from the data task that's what I'm trying to say.

48
00:03:18,450 --> 00:03:23,400
We're going to take the data from the data task and use that to create a UI image then we're going to

49
00:03:23,400 --> 00:03:25,620
set the image view to that.

50
00:03:25,800 --> 00:03:30,510
We're going to stop the animation of the spinner which also hides it and then we're going to set that

51
00:03:30,540 --> 00:03:32,360
image into the image cache.

52
00:03:32,370 --> 00:03:39,540
So it's save there so that if I scroll down on my table view and the image is Decoud the cool thing

53
00:03:39,540 --> 00:03:44,000
is as it comes back up it's just going to be able to instantaneously pull it from the cache.

54
00:03:44,010 --> 00:03:49,710
You're not going to see any weird switching of images here or any kind of glitchy behavior it's just

55
00:03:49,710 --> 00:03:55,080
buttery smooth because everything has already downloaded and saved locally to the image cache on the

56
00:03:55,080 --> 00:03:56,090
device.

57
00:03:56,130 --> 00:04:02,550
Then of course at the end we resume to finish the The download and then our images is saved.

58
00:04:02,700 --> 00:04:08,250
OK the helper's file basically just has a giant array of all of the you or else the images I'm using

59
00:04:08,280 --> 00:04:14,310
pixels which is a very cool Web site for non attribution royalty free images that you can use in any

60
00:04:14,310 --> 00:04:15,980
of your projects.

61
00:04:15,990 --> 00:04:21,530
So those are the you are Elle's those are downloaded by each cell and then displayed in the table view.

62
00:04:21,540 --> 00:04:23,100
So that's the code.

63
00:04:23,100 --> 00:04:25,030
Now we need to add a target for testing.

64
00:04:25,080 --> 00:04:32,010
So go ahead and go up to the top file new target click on that and then scroll down.

65
00:04:32,040 --> 00:04:33,210
Choose iOS.

66
00:04:33,240 --> 00:04:35,000
Unit testing bundle.

67
00:04:35,490 --> 00:04:36,890
And the name photo load test.

68
00:04:36,900 --> 00:04:40,530
That's fine because that is basically what it is we're testing our project.

69
00:04:40,650 --> 00:04:45,190
Click Finish and it'll add in a brand new target right here nice and shiny.

70
00:04:45,370 --> 00:04:51,450
And go ahead and you know open that folder up and then right click we're going to go ahead and basically

71
00:04:51,450 --> 00:04:58,170
create a test file for our view controller because we're going to test the asynchronous operations that

72
00:04:58,170 --> 00:05:01,630
happen when we download a photo.

73
00:05:01,740 --> 00:05:04,880
So go ahead and click new file unit.

74
00:05:04,950 --> 00:05:05,820
Case class.

75
00:05:05,850 --> 00:05:11,650
And then what we're going to do is we're going to name this photo view controller tests.

76
00:05:11,850 --> 00:05:15,240
And of course it's going to be a subclass of X-C test case.

77
00:05:15,240 --> 00:05:23,470
Click next and save it and of course we're presented with the usual screen get rid of the example functions

78
00:05:24,340 --> 00:05:26,130
leave you know set up and tear down.

79
00:05:26,140 --> 00:05:31,780
And we need to call at testable import and our project is called Photo load.

80
00:05:31,780 --> 00:05:36,370
So we're going to import that now since we're testing this new controller we're going to go ahead and

81
00:05:36,370 --> 00:05:40,030
create a variable that will hold the value of our test view controller.

82
00:05:40,060 --> 00:05:40,810
So to do that.

83
00:05:40,810 --> 00:05:41,150
Type.

84
00:05:41,140 --> 00:05:44,060
Var sut meaning system under test.

85
00:05:44,230 --> 00:05:50,320
And that's going to be of type photo the controller put an exclamation mark because we are definitely

86
00:05:50,320 --> 00:05:54,720
going to have a value and we're going to set it up inside of our setup function that's the whole point.

87
00:05:54,730 --> 00:06:02,050
So let's go ahead and create an instance of our storyboard by typing let storyboard equals UI storyboard

88
00:06:02,860 --> 00:06:05,230
the name of course is main.

89
00:06:05,340 --> 00:06:06,890
Whoops that's not right.

90
00:06:06,970 --> 00:06:09,910
The name is main and the bundle is nil.

91
00:06:09,910 --> 00:06:13,040
Then we're going to say Sutt system under test.

92
00:06:13,060 --> 00:06:20,260
We're going to instantiate it by typing storyboard dot instantiate view controller with identifier and

93
00:06:20,260 --> 00:06:25,570
the identifier that I've already set for this project is photo view controller.

94
00:06:26,170 --> 00:06:30,940
And then at the very end to make sure that it comes in as that type we're going to forecast it to be

95
00:06:30,940 --> 00:06:33,340
of type photo view controller.

96
00:06:33,340 --> 00:06:39,350
Then in order to actually get this to work properly as a controller test we're going to call sut dot

97
00:06:39,360 --> 00:06:41,410
load view if needed.

98
00:06:41,440 --> 00:06:42,440
All righty.

99
00:06:42,490 --> 00:06:43,300
Very very cool.

100
00:06:43,300 --> 00:06:48,880
So for the tear down code we're going to go ahead and at the top we're going to type sut equals nil

101
00:06:48,910 --> 00:06:54,610
and we're basically going to just delete that instance of our system under test for every single test.

102
00:06:54,670 --> 00:07:01,360
Now like I said we're going to test an asynchronous operation and that asynchronous operation is our

103
00:07:01,510 --> 00:07:02,700
data task right.

104
00:07:02,800 --> 00:07:07,900
Actually downloading from the Internet and we're going to test to make sure that that works the way

105
00:07:07,900 --> 00:07:08,880
that it's supposed to.

106
00:07:09,190 --> 00:07:19,170
So go ahead and write this test phunk test photo download async operation.

107
00:07:19,270 --> 00:07:19,790
OK.

108
00:07:19,870 --> 00:07:23,440
And let's make some more space here so this is more in the center of the screen.

109
00:07:23,440 --> 00:07:24,430
There we go.

110
00:07:24,820 --> 00:07:25,130
All right.

111
00:07:25,150 --> 00:07:31,840
So instead of comparing the image to the image that's already on the device what we're going to actually

112
00:07:31,840 --> 00:07:36,520
do is we're going to go ahead and use the image that I've saved.

113
00:07:36,520 --> 00:07:43,710
Check it out in the assets folder you'll notice there is the for the photo here pixels photo 7 6 8 2

114
00:07:43,710 --> 00:07:44,270
0 8.

115
00:07:44,330 --> 00:07:47,400
I'm going to copy that and go back in here.

116
00:07:47,470 --> 00:07:51,470
That's going to be our expected image that we are going to attempt to download.

117
00:07:51,880 --> 00:07:55,540
But we're going to go ahead and compare a data type that's easier to compare.

118
00:07:55,840 --> 00:08:00,090
And it's already built into the image so go ahead and let's just check.

119
00:08:00,160 --> 00:08:02,640
Well you know what we should actually write what we're testing.

120
00:08:02,640 --> 00:08:09,730
Test photo download image orientation is identical.

121
00:08:09,730 --> 00:08:14,140
We're going to make sure that the orientation that we expect is actually what results.

122
00:08:14,140 --> 00:08:16,800
So go ahead and type let expected.

123
00:08:16,960 --> 00:08:19,230
Image orientation.

124
00:08:19,390 --> 00:08:26,620
And we're going to go ahead and just call you I image and you image named right here and I'm going to

125
00:08:26,620 --> 00:08:30,970
use the name I just copied pixels photo 7 6 8 2 1 8.

126
00:08:31,240 --> 00:08:36,100
And what we're going to do is we're going to go ahead and access the image orientation which is of type

127
00:08:36,280 --> 00:08:37,840
II image orientation.

128
00:08:38,230 --> 00:08:43,450
Now what we're going to do is we are basically going to use you Aurel session to download our image

129
00:08:43,600 --> 00:08:44,490
the same way that we do.

130
00:08:44,500 --> 00:08:50,380
But I'm going to show you a new thing that you need to know for testing that is regarding asynchronous

131
00:08:50,380 --> 00:08:56,400
code because as you know async code does not always perform exactly the same way every single time.

132
00:08:56,440 --> 00:09:03,190
Sometimes a particular piece or instance or iteration might take a little bit longer than another and

133
00:09:03,250 --> 00:09:04,570
sometimes it might be faster.

134
00:09:04,570 --> 00:09:08,890
So what we're going to do is we're going to use what's called an expectation and I'll talk about that

135
00:09:08,890 --> 00:09:13,470
in just a second but first let's get you are Arel ready by typing.

136
00:09:13,480 --> 00:09:21,130
Guard let you RL And that's going to be equal to you RL And we're going to create a from a string.

137
00:09:21,130 --> 00:09:26,800
Now the string we're going to get that from our helper's file from the image you are all strings array.

138
00:09:26,800 --> 00:09:35,230
And the third item here I guess the fourth item but the item at index 3 is the 7 6 8 2 1 8 image that

139
00:09:35,230 --> 00:09:35,790
we want.

140
00:09:35,800 --> 00:09:37,200
So we're going to go ahead and call.

141
00:09:37,240 --> 00:09:44,730
Image your L strings and pull out the item at the third index just like that.

142
00:09:44,740 --> 00:09:50,890
The reason I'm using guard let here is because you are l comes in is optional and I'm going to make

143
00:09:50,890 --> 00:09:55,630
this a non-optional instance if this works out properly which it will because that's how that's how

144
00:09:55,630 --> 00:09:57,380
it works I promise.

145
00:09:57,520 --> 00:10:04,180
Otherwise we're going to go ahead and say ex-city fail and then Colan return.

146
00:10:04,180 --> 00:10:09,220
So basically our test will fail and then we'll turn if we can't do this.

147
00:10:09,340 --> 00:10:13,320
And now what we're going to do is we're going to create what is called an expectation.

148
00:10:13,320 --> 00:10:16,990
So Type let session answered.

149
00:10:17,010 --> 00:10:17,510
OK.

150
00:10:17,670 --> 00:10:24,000
That's the name of our expectation and let's actually call it that expectation equals and we can call

151
00:10:24,000 --> 00:10:31,050
it a special function called expectation which is of type or I guess returns X-C test expectation.

152
00:10:31,140 --> 00:10:37,740
So give it a name and the name is session right because we're going to be using your L session and so

153
00:10:37,740 --> 00:10:42,480
we're going to make sure that the session answers and returns a result to us now an expectation.

154
00:10:42,480 --> 00:10:50,250
The cool thing is we can basically call this expectation here session answered expectation.

155
00:10:50,250 --> 00:10:56,460
And we can call a little function on it called fullfil and it marks the expectation as having been met.

156
00:10:56,460 --> 00:11:05,910
So as soon as our image is downloaded we can call fullfil and then we can basically say hey our assertion

157
00:11:06,000 --> 00:11:11,310
right we're going to be asserting that you know our image orientation is equal to our expected image

158
00:11:11,310 --> 00:11:12,840
orientation.

159
00:11:12,900 --> 00:11:17,760
We can basically say hey let's wait for a set amount of time maybe five seconds maybe 10 seconds to

160
00:11:17,760 --> 00:11:24,270
make sure that the task finishes even if it's a particularly slow or particularly speedy run through

161
00:11:24,270 --> 00:11:25,510
our asynchronous code.

162
00:11:25,590 --> 00:11:26,850
So go ahead.

163
00:11:26,850 --> 00:11:30,310
And now we're going to actually download our image using your SSN.

164
00:11:30,320 --> 00:11:36,960
So type that URL session shared data task and we're going to choose the option with a u r l and a completion

165
00:11:36,960 --> 00:11:43,770
handler pass in the U R L and then press enter on the completion handler name these like you should.

166
00:11:43,830 --> 00:11:46,310
Data response and error.

167
00:11:46,500 --> 00:11:49,990
And then what we're going to do is we're going to make sure that first we don't have an error.

168
00:11:50,010 --> 00:11:51,840
Let's make sure that we actually did this right.

169
00:11:51,840 --> 00:12:00,580
So if error is not equal to nil we're going to go ahead and say x c t fail.

170
00:12:00,660 --> 00:12:04,060
And the cool thing is we can actually pass in an image here.

171
00:12:04,650 --> 00:12:09,560
Well you know what let's go ahead and let's just say if less error equals error.

172
00:12:09,840 --> 00:12:10,700
And you know what if.

173
00:12:10,740 --> 00:12:12,100
If air comes in is nil.

174
00:12:12,240 --> 00:12:16,050
It's not even going to work so well you know what.

175
00:12:16,050 --> 00:12:24,360
How about this if let air air and air is not equal to know we can pass in our message.

176
00:12:24,390 --> 00:12:29,900
Error dot localized description.

177
00:12:30,030 --> 00:12:37,650
You know what it's given me a problem here it's saying that we're not an optional type cannot be used

178
00:12:38,520 --> 00:12:46,660
as a boolean not trying to use it as a boolean you know.

179
00:12:47,050 --> 00:12:52,060
OK how about why don't we just go with this if let error equals error.

180
00:12:52,060 --> 00:12:56,090
We can go ahead and then use LCT fail erudite localized scription.

181
00:12:56,110 --> 00:12:56,690
OK.

182
00:12:56,860 --> 00:13:02,530
So if we get an error our test will fail and we will get a read out of whatever the error was.

183
00:13:02,530 --> 00:13:04,990
But now we need to go ahead and use the data.

184
00:13:04,990 --> 00:13:11,350
So if let data equals data we're going to create an image now from that data.

185
00:13:11,350 --> 00:13:17,900
So guard let image equals you judge from data.

186
00:13:18,200 --> 00:13:18,520
OK.

187
00:13:18,800 --> 00:13:22,840
And go ahead and pass that data there else.

188
00:13:22,940 --> 00:13:29,020
Ok if that doesn't work we're going to say ex-city fail and then return OK because this is a card we

189
00:13:29,020 --> 00:13:32,050
can return out of it's that we don't get a crash.

190
00:13:32,200 --> 00:13:35,420
I mean of course our test will fail but that's it.

191
00:13:35,800 --> 00:13:39,200
So we now have an image that's great.

192
00:13:39,220 --> 00:13:44,740
Now what we want to do is we basically want to say hey session answer the expectation we can fulfill

193
00:13:44,740 --> 00:13:46,620
it because it's now done.

194
00:13:46,900 --> 00:13:54,430
And what we can do now is finish up our data task by calling dot resume and that's you know you have

195
00:13:54,430 --> 00:13:56,860
to do that for you session all the time.

196
00:13:56,860 --> 00:14:04,060
And now what I can do is I can actually use my expertise assert equal and I can check to see if the

197
00:14:04,060 --> 00:14:07,390
orientation of this image is the same as my expected orientation.

198
00:14:07,390 --> 00:14:14,980
So go ahead and type image dot orientation dot image orientation and then compare that to expected image

199
00:14:14,980 --> 00:14:20,420
orientation and you might be thinking how is this at all different to what we've been doing in the past.

200
00:14:20,440 --> 00:14:22,080
This is where it comes into play.

201
00:14:22,180 --> 00:14:24,010
Wait for expectations.

202
00:14:24,010 --> 00:14:27,670
This powerful function is going to allow us to set a time interval.

203
00:14:27,670 --> 00:14:29,540
I'm going to say eight seconds.

204
00:14:29,680 --> 00:14:30,710
You can choose whatever.

205
00:14:30,910 --> 00:14:36,700
But basically it's going to wait for eight seconds until fulfilling.

206
00:14:36,700 --> 00:14:41,410
And so we're basically setting a delay ensuring that we have enough time to actually download the image

207
00:14:41,410 --> 00:14:46,670
in case it takes a long time maybe your Internet is just really slow for just a few minutes.

208
00:14:46,690 --> 00:14:48,040
Your test would fail.

209
00:14:48,040 --> 00:14:52,810
But this allows you the time to actually make sure that the test passes when it should.

210
00:14:52,810 --> 00:14:58,630
So the completion handler can be nil and we can now run this test so go ahead and press command you

211
00:14:59,020 --> 00:15:00,500
and see how we do.

212
00:15:00,510 --> 00:15:02,730
Art We're going to run our test and see how we do.

213
00:15:02,740 --> 00:15:08,410
Basically we're just making sure that when the photo downloads the orientation comes in the right way.

214
00:15:08,650 --> 00:15:11,200
Running our tests is cool.

215
00:15:11,200 --> 00:15:11,740
Very very cool.

216
00:15:11,740 --> 00:15:13,960
And look at that our test passes.

217
00:15:13,960 --> 00:15:15,680
That's awesome.

218
00:15:15,760 --> 00:15:20,650
You know what to make sure that this actually works the way that it's supposed to do what I can do is

219
00:15:20,650 --> 00:15:26,380
I could change something you know that might very very minimally modify the code.

220
00:15:26,610 --> 00:15:30,610
Of course this image name is you know 7 6 8 2 1 8.

221
00:15:30,610 --> 00:15:31,940
Let's change it to 7.

222
00:15:31,960 --> 00:15:33,130
That file doesn't exist.

223
00:15:33,130 --> 00:15:37,180
So if I run this we'll make sure that we're actually getting the results that we want.

224
00:15:37,180 --> 00:15:39,070
We're just going to make sure our tests work.

225
00:15:39,160 --> 00:15:45,520
It's testing it's running and our test should fail of course because that image is not even there so

226
00:15:45,520 --> 00:15:48,210
it won't be able to compare it and look at that.

227
00:15:48,550 --> 00:15:51,790
The image is not equal to the image it says it's not even there.

228
00:15:51,790 --> 00:15:53,330
So very very cool.

229
00:15:53,410 --> 00:15:57,640
If we run it again we'll see that our test really does work and it really is checking to see if the

230
00:15:57,640 --> 00:16:00,310
orientations are equal which is really neat.

231
00:16:00,580 --> 00:16:01,260
OK.

232
00:16:01,630 --> 00:16:06,970
Three two one done hopefully maybe an hour and it passes.

233
00:16:06,970 --> 00:16:08,790
Look at that super super awesome.

234
00:16:08,950 --> 00:16:14,560
So guys this is just one example of how you can test asynchronous operations by using what are called

235
00:16:14,590 --> 00:16:15,500
expectations.

236
00:16:15,520 --> 00:16:22,630
You can set an expectation say when it is fulfilled and then give it a time out time so that you can

237
00:16:22,630 --> 00:16:23,920
actually wait for it to process.

238
00:16:23,920 --> 00:16:28,050
This is really really helpful when testing network code especially live network code.

239
00:16:28,090 --> 00:16:32,680
So let's go ahead and let's head over to the next video where we're going to use what is called a mock

240
00:16:32,890 --> 00:16:38,710
K it's like our own version of an already created class and we're going to be able to use that mock

241
00:16:39,100 --> 00:16:45,490
to basically test our server to make sure that everything is running and making sure that if someone

242
00:16:45,490 --> 00:16:50,830
were to accidentally change the text of a certain endpoint the test would fail making sure that we don't

243
00:16:50,830 --> 00:16:56,050
have any weird server crashes or tricky bugs to fix or we're going to look into creating a mock u r

244
00:16:56,050 --> 00:17:03,370
l session and making sure that our our host and our paths and our end points are all working the way

245
00:17:03,370 --> 00:17:05,500
that we're supposed to in this particular application.

246
00:17:05,500 --> 00:17:07,540
So I'll see you there.
