1 00:00:06,600 --> 00:00:09,330 - When you are writing professional code, 2 00:00:09,330 --> 00:00:12,720 it's important that you cover all the branches 3 00:00:12,720 --> 00:00:14,190 that your code executes. 4 00:00:14,190 --> 00:00:16,710 So, for example, in a realistic application, 5 00:00:16,710 --> 00:00:19,320 in your code you have lots of if statements 6 00:00:19,320 --> 00:00:23,490 and switch statements and loops and function calls, 7 00:00:23,490 --> 00:00:26,400 and you wanna make sure that your test 8 00:00:26,400 --> 00:00:30,750 covers all possible execution paths through that code. 9 00:00:30,750 --> 00:00:32,400 Okay? To make sure that there isn't some bug 10 00:00:32,400 --> 00:00:35,730 lurkin' somewhere that you haven't quite discovered yet. 11 00:00:35,730 --> 00:00:40,080 So, test coverage turns out to be a really important topic 12 00:00:40,080 --> 00:00:42,630 in industrial strength testing. 13 00:00:42,630 --> 00:00:44,910 So, we're gonna look at how to generate 14 00:00:44,910 --> 00:00:48,420 a test coverage report using Jest. 15 00:00:48,420 --> 00:00:49,860 The example that we're going to look at 16 00:00:49,860 --> 00:00:52,053 is in the test coverage folder, 17 00:00:53,790 --> 00:00:57,870 so JsTdd lesson three, test coverage, 18 00:00:57,870 --> 00:01:00,720 and if you look in there, you'll find, 19 00:01:00,720 --> 00:01:02,770 first of all, there are two source files. 20 00:01:04,020 --> 00:01:05,130 We'll have a look at the source file 21 00:01:05,130 --> 00:01:07,170 on the right, first of all. 22 00:01:07,170 --> 00:01:10,530 It's to do with exam marks, so it's a very simple scenario, 23 00:01:10,530 --> 00:01:13,890 but there's quite a lot of different execution paths 24 00:01:13,890 --> 00:01:16,950 in that function and in that function. 25 00:01:16,950 --> 00:01:18,570 So, first of all isValid. 26 00:01:18,570 --> 00:01:22,050 The idea is that it receives an exam mark, 27 00:01:22,050 --> 00:01:25,110 and the first thing it does is to check if it's too low. 28 00:01:25,110 --> 00:01:28,320 If it's less than zero, it's an invalid mark. It's too low. 29 00:01:28,320 --> 00:01:30,990 So, it returns a string to indicate the status. 30 00:01:30,990 --> 00:01:34,560 No, that's not a valid mark. It's too low. 31 00:01:34,560 --> 00:01:38,670 Maybe the mark is too high or maybe it's okay. 32 00:01:38,670 --> 00:01:41,940 So, we have three possible execution paths 33 00:01:41,940 --> 00:01:43,050 through that function. 34 00:01:43,050 --> 00:01:45,240 We export that function from here, 35 00:01:45,240 --> 00:01:47,550 and then we import it over here. 36 00:01:47,550 --> 00:01:50,760 So, getGrade is like a higher level function. 37 00:01:50,760 --> 00:01:52,350 It receives the exam mark, 38 00:01:52,350 --> 00:01:54,870 and the first thing it does is to validate it. 39 00:01:54,870 --> 00:01:58,500 It calls the isValid function from over here 40 00:01:58,500 --> 00:02:00,450 and hopefully it's okay. 41 00:02:00,450 --> 00:02:03,050 It checks is the status not okay? 42 00:02:03,050 --> 00:02:05,460 In which case it returns the error message. 43 00:02:05,460 --> 00:02:07,737 Okay? Like, "Too low" or "Too high." 44 00:02:08,820 --> 00:02:12,210 Assumin' that the value is okay, then we proceed, 45 00:02:12,210 --> 00:02:14,190 and the way it works in this example, 46 00:02:14,190 --> 00:02:17,430 if your exam mark is greater than or equal to 70, 47 00:02:17,430 --> 00:02:19,320 you get an A grade. 48 00:02:19,320 --> 00:02:21,000 The purpose of this function is to tell you 49 00:02:21,000 --> 00:02:23,520 what grade you've got for a given mark. 50 00:02:23,520 --> 00:02:28,050 If your mark is between 60 and 69, 51 00:02:28,050 --> 00:02:32,640 then you get a B, 50 to 59 is a C, 52 00:02:32,640 --> 00:02:34,310 and below 50 is a fail. Okay? 53 00:02:34,310 --> 00:02:37,650 So, you can see there are various different execution paths 54 00:02:37,650 --> 00:02:39,663 through this function as well. 55 00:02:40,950 --> 00:02:45,780 Okay. Right. Now, here are my tests. 56 00:02:45,780 --> 00:02:48,750 I've got a test suite, which tests... 57 00:02:48,750 --> 00:02:51,360 well, I think it's done a decent job. 58 00:02:51,360 --> 00:02:54,540 I'm checkin' that if I get over 70, 59 00:02:54,540 --> 00:02:56,400 it should gimme an A grade. 60 00:02:56,400 --> 00:02:59,670 Exactly 70 should also be an A grade. 61 00:02:59,670 --> 00:03:02,253 60 to 69 should give me a B, 62 00:03:03,152 --> 00:03:06,390 50 to 59 should give me a C. 63 00:03:06,390 --> 00:03:11,390 So, do these tests cover all execution paths? 64 00:03:11,580 --> 00:03:13,050 Let's actually have a look at the proper code 65 00:03:13,050 --> 00:03:15,120 before we answer that question. 66 00:03:15,120 --> 00:03:19,830 In my text editor, here's my example code. 67 00:03:19,830 --> 00:03:21,720 Well, first of all, here's my utility code, 68 00:03:21,720 --> 00:03:24,777 here's my isValid function that tells me, 69 00:03:24,777 --> 00:03:27,660 you know, is it even a valid mark? 70 00:03:27,660 --> 00:03:30,930 And then my higher order function, in example.js, 71 00:03:30,930 --> 00:03:33,460 is where I have my getGrade function 72 00:03:34,320 --> 00:03:36,660 call the isValid function, first of all, 73 00:03:36,660 --> 00:03:38,610 determine if it's not valid. 74 00:03:38,610 --> 00:03:41,103 If it is valid, then tell me what grade I've got. 75 00:03:42,510 --> 00:03:44,553 Okay? And then these are my tests. 76 00:03:45,690 --> 00:03:50,040 So, this suite of tests is deliberately insufficient. 77 00:03:50,040 --> 00:03:54,877 It doesn't test for invalid marks like -1 or 101, 78 00:03:55,800 --> 00:03:58,050 and it doesn't check either, 79 00:03:58,050 --> 00:04:01,050 it doesn't test either if you got less than 50. 80 00:04:01,050 --> 00:04:03,210 So, when I wrote these tests, 81 00:04:03,210 --> 00:04:05,670 maybe I didn't realize that I've missed 82 00:04:05,670 --> 00:04:08,040 some paths of execution through the code. 83 00:04:08,040 --> 00:04:12,900 Have a look. This first test function is testin' 75. 84 00:04:12,900 --> 00:04:15,900 That's an A, then it's testin' 70. 85 00:04:15,900 --> 00:04:18,270 Well, that should also be an A. 86 00:04:18,270 --> 00:04:22,620 65 should be a B and so should 60. 87 00:04:22,620 --> 00:04:27,620 55 should be a C and so should 50. 88 00:04:27,720 --> 00:04:32,070 But notice that it's not checkin' for an out of range value. 89 00:04:32,070 --> 00:04:34,260 My tests are insufficient. 90 00:04:34,260 --> 00:04:36,780 It's not checkin' for a too low value, 91 00:04:36,780 --> 00:04:38,883 a too high value or for a fail. 92 00:04:40,080 --> 00:04:44,160 How can I get Jest to help me realize 93 00:04:44,160 --> 00:04:46,620 that I've missed some execution paths? 94 00:04:46,620 --> 00:04:49,353 Well, that's where test coverage comes into play. 95 00:04:50,580 --> 00:04:54,000 With Jest, you can run a test in coverage mode. 96 00:04:54,000 --> 00:04:56,430 You set the coverage option to be true. 97 00:04:56,430 --> 00:04:59,640 It's --coverage=true. 98 00:04:59,640 --> 00:05:02,280 Okay? And when you do that, it'll run the tests, 99 00:05:02,280 --> 00:05:04,980 but it'll also give you back a visual report 100 00:05:04,980 --> 00:05:08,640 to indicate any paths that you might have forgotten. 101 00:05:08,640 --> 00:05:10,410 So, let's run that test and see 102 00:05:10,410 --> 00:05:12,060 what the error report looks like. 103 00:05:13,050 --> 00:05:17,280 So, "npm run test" and then options. 104 00:05:17,280 --> 00:05:20,787 So, the option is --coverage=true. 105 00:05:23,040 --> 00:05:27,510 So, run the tests and give me a coverage report 106 00:05:27,510 --> 00:05:29,910 to let me know about any paths that I've missed. 107 00:05:30,900 --> 00:05:33,873 So, we just have to wait for this to complete its task. 108 00:05:34,860 --> 00:05:37,740 So, it's running through my five test functions. 109 00:05:37,740 --> 00:05:40,680 They all worked, or there were six tests here. 110 00:05:40,680 --> 00:05:44,583 They all worked, but I get this coverage report. 111 00:05:45,750 --> 00:05:48,240 File by file, it'll tell me... 112 00:05:48,240 --> 00:05:51,330 So, for example, in util.js, 113 00:05:51,330 --> 00:05:55,440 it executed two thirds of the statements 114 00:05:55,440 --> 00:05:57,300 and half of the branches. 115 00:05:57,300 --> 00:05:58,850 Now, that's not so good, is it? 116 00:05:59,880 --> 00:06:02,700 It executed all the functions in that file. 117 00:06:02,700 --> 00:06:04,400 Well, there was only one function, 118 00:06:05,790 --> 00:06:10,290 but only 66%, two thirds of the lines. 119 00:06:10,290 --> 00:06:11,880 This is the useful thing here. 120 00:06:11,880 --> 00:06:15,450 It tells me which lines in that file 121 00:06:15,450 --> 00:06:18,480 weren't executed through my test path. 122 00:06:18,480 --> 00:06:23,480 Line three and line five weren't covered by my tests. 123 00:06:25,290 --> 00:06:28,620 Okay? So, line three and line five were skipped, 124 00:06:28,620 --> 00:06:32,310 weren't executed by my tests in util.js. 125 00:06:32,310 --> 00:06:35,370 Oh, well, let's have a look at util.js. 126 00:06:35,370 --> 00:06:39,360 In util.js, it says line three was never- 127 00:06:39,360 --> 00:06:42,390 my tests never took me down that path. 128 00:06:42,390 --> 00:06:45,750 Okay, so that tells me my testin' is insufficient 129 00:06:45,750 --> 00:06:48,930 because I haven't passed in a test 130 00:06:48,930 --> 00:06:50,790 that would execute that path. 131 00:06:50,790 --> 00:06:53,580 I would need to add another test into my test suite 132 00:06:53,580 --> 00:06:56,640 where I passed in a value of -1. 133 00:06:56,640 --> 00:06:59,730 And what was the other line that was a problem? 134 00:06:59,730 --> 00:07:03,090 Oh, line five was also not covered. 135 00:07:03,090 --> 00:07:05,790 Line five is this one, okay? 136 00:07:05,790 --> 00:07:08,310 So, I haven't gone through this branch either. 137 00:07:08,310 --> 00:07:11,280 I haven't given it a value like 101. 138 00:07:11,280 --> 00:07:13,080 I would need to go back to my test suite 139 00:07:13,080 --> 00:07:15,543 and add those functions into my test suite. 140 00:07:17,220 --> 00:07:21,600 Okay. What about the other one then, example.js? 141 00:07:21,600 --> 00:07:24,783 So, that had 75% of the branches covered, 142 00:07:25,680 --> 00:07:30,600 but line seven wasn't covered, neither was line 17. 143 00:07:30,600 --> 00:07:33,090 So, let's have a look at those and see why. 144 00:07:33,090 --> 00:07:36,810 So, in example.js, line seven, 145 00:07:36,810 --> 00:07:39,480 my tests never took it down that path. 146 00:07:39,480 --> 00:07:41,220 Well, yes, that makes sense, doesn't it? 147 00:07:41,220 --> 00:07:44,790 Because in my examples, I just gave it valid exam marks, 148 00:07:44,790 --> 00:07:49,290 75, 70, 65, 60, 55, 50, 149 00:07:49,290 --> 00:07:51,510 but I never gave it- I never tested 150 00:07:51,510 --> 00:07:53,760 an invalid mark, like -1, 151 00:07:53,760 --> 00:07:57,513 so my tests never took it down that path. 152 00:07:58,560 --> 00:08:00,420 Right. So, line seven was wasn't covered. 153 00:08:00,420 --> 00:08:03,870 Neither was line 17. Line 17... 154 00:08:03,870 --> 00:08:08,280 Ah. Look! My tests didn't take me down the "fail" route. 155 00:08:08,280 --> 00:08:12,240 I didn't have a test, for example, with a value like 45. 156 00:08:12,240 --> 00:08:14,100 This is really useful. 157 00:08:14,100 --> 00:08:15,420 There's no way, otherwise, 158 00:08:15,420 --> 00:08:18,300 we'd know that our tests haven't 159 00:08:18,300 --> 00:08:20,010 been executed thoroughly enough 160 00:08:20,010 --> 00:08:23,370 or that our tests don't cover all the execution paths. 161 00:08:23,370 --> 00:08:26,400 This would tell me what tests I need to add 162 00:08:26,400 --> 00:08:29,820 to take my tests down these extra paths. 163 00:08:29,820 --> 00:08:30,653 So, it's really nice. 164 00:08:30,653 --> 00:08:32,040 When you run in coverage mode, 165 00:08:32,040 --> 00:08:35,163 you get this visual appearance that you can see. 166 00:08:36,960 --> 00:08:38,793 You can also generate a report. 167 00:08:39,690 --> 00:08:43,890 So, you can see with the coverage option, 168 00:08:43,890 --> 00:08:47,100 it's generated a folder called "coverage," 169 00:08:47,100 --> 00:08:51,240 and in there, there's a file called "coverage-final.json." 170 00:08:51,240 --> 00:08:53,910 It's summary information like we've just seen, 171 00:08:53,910 --> 00:08:56,700 but in JSON format that you can use 172 00:08:56,700 --> 00:09:00,090 for automatin' your procedures, for example. 173 00:09:00,090 --> 00:09:01,950 Rather than just having to look at the screen, 174 00:09:01,950 --> 00:09:04,860 you can write some code that looks at this JSON format 175 00:09:04,860 --> 00:09:07,200 and does somethin' automated. 176 00:09:07,200 --> 00:09:09,330 So, my test coverage folder, 177 00:09:09,330 --> 00:09:11,823 it generated the coverage sub-folder, 178 00:09:13,080 --> 00:09:17,607 and in there I have "coverage-final.json." 179 00:09:18,600 --> 00:09:19,500 Let's have a look. 180 00:09:22,200 --> 00:09:24,270 So, a JSON format file, 181 00:09:24,270 --> 00:09:28,110 which gives me a summary of my test run. 182 00:09:28,110 --> 00:09:29,403 Okay? So, here we go. 183 00:09:30,240 --> 00:09:34,350 If you scroll through, it gives you complete information 184 00:09:34,350 --> 00:09:36,027 about what tests were executed 185 00:09:36,027 --> 00:09:38,730 and what tests were not executed. 186 00:09:38,730 --> 00:09:40,580 There's a lot of information in here. 187 00:09:41,790 --> 00:09:42,933 Woof, look at that. 188 00:09:44,400 --> 00:09:48,120 Okay, so it is all there. 189 00:09:48,120 --> 00:09:51,960 That's the definitive summary of what happened. 190 00:09:51,960 --> 00:09:54,990 Not the easiest thing to read, but it is there. 191 00:09:54,990 --> 00:09:59,553 That's the complete snapshot of how the tests actually went. 192 00:10:01,620 --> 00:10:04,440 There's also a clover.xml, 193 00:10:04,440 --> 00:10:07,983 which displays summary information in XML format. 194 00:10:09,270 --> 00:10:14,270 Clover.xml. So, if I open that one up in a text editor... 195 00:10:16,050 --> 00:10:18,300 I was tempted to open it up in a browser window there, 196 00:10:18,300 --> 00:10:21,723 but you can just open up the XML file in a code editor. 197 00:10:23,010 --> 00:10:26,820 And here, let me just give myself a bit more space, 198 00:10:26,820 --> 00:10:29,940 you can see the files that were tested. 199 00:10:29,940 --> 00:10:32,133 So, that was the example.js file, 200 00:10:34,560 --> 00:10:39,560 and it kind of tells you what line was executed, 201 00:10:41,220 --> 00:10:45,150 and you can kind of see here as well that, 202 00:10:45,150 --> 00:10:47,253 for the other file, util.js, 203 00:10:48,750 --> 00:10:50,100 you can see here the metrics 204 00:10:50,100 --> 00:10:53,550 of how many statements there were, how many were covered, 205 00:10:53,550 --> 00:10:56,130 how many conditionals there were, how many you covered, 206 00:10:56,130 --> 00:10:57,600 how many methods were covered, 207 00:10:57,600 --> 00:10:59,913 and how many were there in total. 208 00:11:00,840 --> 00:11:03,543 Okay, and for each line, it can tell you, 209 00:11:04,590 --> 00:11:07,200 you know, how many times it went down the if branch 210 00:11:07,200 --> 00:11:08,613 and what the results were. 211 00:11:09,660 --> 00:11:12,960 So, there's full information here if you want to dig in, 212 00:11:12,960 --> 00:11:15,930 or if you prefer, you can just use the visual feedback 213 00:11:15,930 --> 00:11:17,580 as an indicator. 214 00:11:17,580 --> 00:11:20,010 That's probably enough, initially, 215 00:11:20,010 --> 00:11:22,650 to give you an idea about where you need to add tests. 216 00:11:22,650 --> 00:11:26,130 What you then do is add tests back in 217 00:11:26,130 --> 00:11:28,440 and then run it in coverage mode again, 218 00:11:28,440 --> 00:11:29,640 and, hopefully, this time, 219 00:11:29,640 --> 00:11:32,130 you'll find that everything's been covered properly 220 00:11:32,130 --> 00:11:35,553 and your test is complete, and then you can have a rest.