1 00:00:06,570 --> 00:00:11,570 - In a realistic application, things go wrong, errors occur. 2 00:00:11,700 --> 00:00:13,560 And it's important that we cater 3 00:00:13,560 --> 00:00:15,810 for these errors in our tests. 4 00:00:15,810 --> 00:00:17,730 If we expect an error to occur 5 00:00:17,730 --> 00:00:20,853 we can test that it does occur when expected. 6 00:00:21,780 --> 00:00:24,360 This becomes a bit more complicated when the code 7 00:00:24,360 --> 00:00:28,560 that we're trying to invoke is long running or asynchronous. 8 00:00:28,560 --> 00:00:31,380 And if it's gonna take five seconds to complete an operation 9 00:00:31,380 --> 00:00:36,180 and then it fails, how do we test for that in our code? 10 00:00:36,180 --> 00:00:38,580 First of all, how do we generate the error 11 00:00:38,580 --> 00:00:41,700 from our long-running code to say it failed? 12 00:00:41,700 --> 00:00:44,220 And how can we test that that error occurred 13 00:00:44,220 --> 00:00:45,990 when we writing our tests. 14 00:00:45,990 --> 00:00:49,290 So it turns out to be a bit tricky. 15 00:00:49,290 --> 00:00:51,450 And that's what we're going to look at in this lesson. 16 00:00:51,450 --> 00:00:53,190 So if you want to have a look. 17 00:00:53,190 --> 00:00:57,180 Testing call back errors is the folder, in the demo folder. 18 00:00:57,180 --> 00:01:01,740 So in lesson four, testing callbacks errors. 19 00:01:01,740 --> 00:01:05,370 And if you open that in the code editor, it looks like this. 20 00:01:05,370 --> 00:01:10,370 So we have some code in operations JS, which raises errors. 21 00:01:12,870 --> 00:01:14,370 And then we'll see how to test 22 00:01:14,370 --> 00:01:17,430 for those errors in operations test. 23 00:01:17,430 --> 00:01:20,160 As in the previous section, we'll see how to do it 24 00:01:20,160 --> 00:01:23,880 incorrectly first and describe why it doesn't work. 25 00:01:23,880 --> 00:01:26,550 And they would see how to do it correctly afterwards. 26 00:01:26,550 --> 00:01:29,760 Okay, so here is an incorrect approach 27 00:01:29,760 --> 00:01:33,330 to raising an error when something goes wrong 28 00:01:33,330 --> 00:01:35,553 in a long-running piece of code. 29 00:01:36,660 --> 00:01:39,060 So have a look what's going on here. 30 00:01:39,060 --> 00:01:42,690 In my operations JS I've got a slow operation with an error. 31 00:01:42,690 --> 00:01:44,760 This is the incorrect way to do it. 32 00:01:44,760 --> 00:01:47,970 The incorrect way to signal an error 33 00:01:47,970 --> 00:01:50,100 when you have long-running code. 34 00:01:50,100 --> 00:01:51,510 I'll explain what I was trying to do 35 00:01:51,510 --> 00:01:53,910 and then I'll explain why it's not good enough. 36 00:01:53,910 --> 00:01:57,810 So the idea is the function takes in a couple of parameters 37 00:01:57,810 --> 00:02:01,260 and will report back the result eventually. 38 00:02:01,260 --> 00:02:03,480 So I have a set time out again 39 00:02:03,480 --> 00:02:05,853 to kind of simulate long-running work. 40 00:02:07,170 --> 00:02:09,270 So when I call this function, 41 00:02:09,270 --> 00:02:13,500 it actually returns immediately, but it kicks off a timer. 42 00:02:13,500 --> 00:02:16,590 So after two seconds, this code here, 43 00:02:16,590 --> 00:02:20,820 this lambda starting there and terminating there, 44 00:02:20,820 --> 00:02:23,550 after two seconds, that will be invoked. 45 00:02:23,550 --> 00:02:27,783 And at this point it checks the second parameter. 46 00:02:28,710 --> 00:02:31,110 It's gonna do a divide, if B is zero 47 00:02:31,110 --> 00:02:33,993 it shows an error to say you can't divide by zero. 48 00:02:35,130 --> 00:02:39,390 So otherwise it caused me back to gimme the result. 49 00:02:39,390 --> 00:02:42,990 So what I was hoping to achieve was either an error 50 00:02:42,990 --> 00:02:46,320 would be passed back to the caller or the result 51 00:02:46,320 --> 00:02:49,320 would be passed back to the caller if possible. 52 00:02:49,320 --> 00:02:51,300 Now this isn't gonna work. 53 00:02:51,300 --> 00:02:54,480 The problem is this function is gonna terminate 54 00:02:54,480 --> 00:02:57,843 immediately having kicked off a two-second delay. 55 00:02:59,970 --> 00:03:04,970 So when this call back is invoked two seconds later, 56 00:03:07,230 --> 00:03:10,380 the code that runs this isn't invoked 57 00:03:10,380 --> 00:03:12,849 in the context of a try/catch block. 58 00:03:12,849 --> 00:03:16,080 What we'll actually find when we run this in a moment 59 00:03:16,080 --> 00:03:19,860 is that this code is invoked by jest 60 00:03:19,860 --> 00:03:23,530 two seconds later when the timer occurred 61 00:03:24,826 --> 00:03:28,923 and jest won't be able to process the error message. 62 00:03:30,330 --> 00:03:34,050 So let's see what happens if we try to test it. 63 00:03:34,050 --> 00:03:37,320 Here's a naive attempt to test that function. 64 00:03:37,320 --> 00:03:38,920 Okay, so this won't work either. 65 00:03:40,740 --> 00:03:44,160 Okay, so I try to call that function 66 00:03:44,160 --> 00:03:46,623 and maybe it'll throw an error. 67 00:03:48,619 --> 00:03:51,240 Well, it should throw an error, in which case I would expect 68 00:03:51,240 --> 00:03:54,150 the error message to be "Cannot divide by zero." 69 00:03:54,150 --> 00:03:55,923 And then we say we're done. 70 00:03:57,540 --> 00:04:00,480 This is not gonna work. 71 00:04:00,480 --> 00:04:02,880 Okay, when it calls that function, 72 00:04:02,880 --> 00:04:06,810 this function actually returns immediately. 73 00:04:06,810 --> 00:04:09,450 It sets up a two-second delay 74 00:04:09,450 --> 00:04:11,340 but the function itself returns immediately. 75 00:04:11,340 --> 00:04:13,980 So immediately that function returns, 76 00:04:13,980 --> 00:04:15,380 and then we say, we're done. 77 00:04:16,770 --> 00:04:20,973 We didn't here wait the two seconds for that to finish, 78 00:04:21,930 --> 00:04:24,240 that function will return immediately. 79 00:04:24,240 --> 00:04:25,890 And then we say, we're done. 80 00:04:25,890 --> 00:04:29,310 So this catch handler would only catch an error 81 00:04:29,310 --> 00:04:31,470 if our function threw it immediately. 82 00:04:31,470 --> 00:04:34,140 It doesn't catch errors that occur 83 00:04:34,140 --> 00:04:35,790 because of timers two seconds later, 84 00:04:35,790 --> 00:04:37,593 it's just not the right mechanism. 85 00:04:38,970 --> 00:04:42,810 So we've tried to put our function call in the try/catch 86 00:04:42,810 --> 00:04:44,910 but this would only catch exceptions 87 00:04:44,910 --> 00:04:48,300 thrown synchronously from that function. 88 00:04:48,300 --> 00:04:51,420 It isn't going to be around two seconds later 89 00:04:51,420 --> 00:04:55,623 when my timer raises the error. 90 00:04:56,730 --> 00:05:01,560 So this is what's gonna happen if we try to run the result. 91 00:05:01,560 --> 00:05:05,130 Jest minus three, the name of that test function 92 00:05:05,130 --> 00:05:07,770 was "test-incorrect technique." 93 00:05:07,770 --> 00:05:10,807 I'm gonna see what errors occur. 94 00:05:10,807 --> 00:05:14,553 "Detect open handles" remember that from the previous demo? 95 00:05:15,660 --> 00:05:18,120 So it looks like this, when you run the test, 96 00:05:18,120 --> 00:05:20,430 it'll display an error message basically. 97 00:05:20,430 --> 00:05:23,100 It'll run my test function. 98 00:05:23,100 --> 00:05:25,890 My test function will terminate immediately 99 00:05:25,890 --> 00:05:28,620 but jest will realize that there's some other code 100 00:05:28,620 --> 00:05:32,250 going on, the two-second timeout. 101 00:05:32,250 --> 00:05:35,790 Two seconds later jest will invoke that code 102 00:05:35,790 --> 00:05:40,790 and it'll realize that it then raised an error long after 103 00:05:41,700 --> 00:05:45,363 my try/catch block had finished in my test function. 104 00:05:46,200 --> 00:05:51,183 So this mechanism just doesn't work, it just doesn't work. 105 00:05:52,830 --> 00:05:56,130 My test code here, we'll call that function, 106 00:05:56,130 --> 00:05:58,953 but not wait and it'll immediately say done. 107 00:05:59,790 --> 00:06:03,750 Two seconds later that function will throw an error 108 00:06:03,750 --> 00:06:05,850 but this test function's already finished. 109 00:06:05,850 --> 00:06:07,320 Who handles the error? 110 00:06:07,320 --> 00:06:10,236 Well, nobody really, it ends up being displayed 111 00:06:10,236 --> 00:06:15,236 as an uncaught error in the console window. 112 00:06:18,210 --> 00:06:20,613 So that didn't really get us very far. 113 00:06:21,660 --> 00:06:25,920 Throwing an error from a long-running function never works. 114 00:06:25,920 --> 00:06:29,220 You can't do it because there isn't anyone around 115 00:06:29,220 --> 00:06:32,160 to catch the error, you have to have a different approach. 116 00:06:32,160 --> 00:06:33,960 You can't just throw an error 117 00:06:33,960 --> 00:06:36,210 from your long-running function. 118 00:06:36,210 --> 00:06:39,030 What you have to do instead is to provide 119 00:06:39,030 --> 00:06:41,160 or to receive two separate callbacks. 120 00:06:41,160 --> 00:06:45,600 If we look at any production code, 121 00:06:45,600 --> 00:06:48,630 like the fetch function for example, 122 00:06:48,630 --> 00:06:51,573 you can pass in two callback functions. 123 00:06:52,770 --> 00:06:54,690 A callback function that would be invoked 124 00:06:54,690 --> 00:06:57,390 if everything worked satisfactorily. 125 00:06:57,390 --> 00:07:00,900 And a call back function to invoke, if it doesn't. 126 00:07:00,900 --> 00:07:02,760 So this is the correct way to do it. 127 00:07:02,760 --> 00:07:05,700 This is a slow operation written properly. 128 00:07:05,700 --> 00:07:07,770 It takes in a couple of parameters. 129 00:07:07,770 --> 00:07:09,630 It receives a call back function 130 00:07:09,630 --> 00:07:11,880 that it can invoke if it's okay 131 00:07:11,880 --> 00:07:15,180 and a separate call back function, it can evoke if it isn't. 132 00:07:15,180 --> 00:07:20,040 So in my slow operation, it returns immediately 133 00:07:20,040 --> 00:07:22,710 but it kicks off a delayed reaction basically. 134 00:07:22,710 --> 00:07:24,960 Two seconds later, this code will execute. 135 00:07:24,960 --> 00:07:29,640 This lambda here will execute after two seconds. 136 00:07:29,640 --> 00:07:32,430 And after two seconds, then it'll either 137 00:07:32,430 --> 00:07:35,560 call the error call back with an error message 138 00:07:36,420 --> 00:07:41,280 or it'll call the okay call back with the actual result. 139 00:07:41,280 --> 00:07:44,940 So either the call level has that call back function invoked 140 00:07:44,940 --> 00:07:47,403 or that one, and it's quite deterministic. 141 00:07:48,270 --> 00:07:51,720 So to call that function properly from our test 142 00:07:51,720 --> 00:07:52,860 it would look like this. 143 00:07:52,860 --> 00:07:54,813 This is my correct approach. 144 00:07:55,950 --> 00:07:57,690 I have a callback function here. 145 00:07:57,690 --> 00:08:00,450 Now, this particular test is just testing 146 00:08:00,450 --> 00:08:02,130 that an error occurred. 147 00:08:02,130 --> 00:08:03,780 So when I call the function, 148 00:08:03,780 --> 00:08:07,740 I pass in the number A and the number B. 149 00:08:07,740 --> 00:08:11,070 I know because I've passed in a value of zero for B 150 00:08:11,070 --> 00:08:14,643 that my function here will go down the error path. 151 00:08:15,480 --> 00:08:18,090 So I don't bother passing in a callback function 152 00:08:18,090 --> 00:08:22,650 for the okay result because I've given it zero here, 153 00:08:22,650 --> 00:08:24,000 we know it's gonna fail. 154 00:08:24,000 --> 00:08:27,510 We know it's gonna go down the error condition path. 155 00:08:27,510 --> 00:08:31,110 So I give it an error call back instead. 156 00:08:31,110 --> 00:08:34,710 It'll call that function, that function returns immediately. 157 00:08:34,710 --> 00:08:37,470 Oh, by the way, notice that my test here 158 00:08:37,470 --> 00:08:40,650 has a done parameter, so that jest will wait 159 00:08:40,650 --> 00:08:44,160 until I call that function to move on. 160 00:08:44,160 --> 00:08:48,420 So first of all, I invoke my slow operation, 161 00:08:48,420 --> 00:08:49,530 give it some parameters. 162 00:08:49,530 --> 00:08:53,430 That function will terminate, two seconds later 163 00:08:53,430 --> 00:08:58,050 it will call my test error and in my test error, 164 00:08:58,050 --> 00:09:00,660 I can validate the error message that occurred, 165 00:09:00,660 --> 00:09:03,870 cannot divide by zero, and then I can say done. 166 00:09:03,870 --> 00:09:05,700 Okay, so this is the correct way 167 00:09:05,700 --> 00:09:08,340 for checking that errors have occurred. 168 00:09:08,340 --> 00:09:13,170 You invoke the function, you give it maybe an okay callback, 169 00:09:13,170 --> 00:09:15,240 definitely an error callback. 170 00:09:15,240 --> 00:09:17,700 In your error callback you can get the error message 171 00:09:17,700 --> 00:09:21,510 that was passed back to you in a deterministic manner. 172 00:09:21,510 --> 00:09:23,460 It's not used exception handling, 173 00:09:23,460 --> 00:09:25,320 it's using callback functions. 174 00:09:25,320 --> 00:09:26,700 We can then check the error, 175 00:09:26,700 --> 00:09:29,250 string to make sure that it's correct. 176 00:09:29,250 --> 00:09:32,460 And then we can say to jest, now we are done 177 00:09:32,460 --> 00:09:35,190 and jest will be quite happy with that. 178 00:09:35,190 --> 00:09:36,600 Okay, so there we go. 179 00:09:36,600 --> 00:09:38,340 So when I called the function, 180 00:09:38,340 --> 00:09:40,083 I passed in an error call back. 181 00:09:41,130 --> 00:09:44,310 That error call back would be in vote two seconds later. 182 00:09:44,310 --> 00:09:47,127 And then that error call back I can ensure 183 00:09:47,127 --> 00:09:49,950 that the result was correct. 184 00:09:49,950 --> 00:09:53,580 I can run the test like this, jest minus T. 185 00:09:53,580 --> 00:09:56,880 The name of this test function, "test-correct technique." 186 00:09:56,880 --> 00:09:59,130 And if you do that then we'll find 187 00:09:59,130 --> 00:10:01,410 that it does actually work correctly. 188 00:10:01,410 --> 00:10:05,280 It calls my function, it waits two seconds 189 00:10:05,280 --> 00:10:07,950 until my error call back is invoked, 190 00:10:07,950 --> 00:10:12,693 and then it validates the error message and succeeds.