1 00:00:00,06 --> 00:00:02,08 [Instructordomain-specific language or DSL 2 00:00:02,08 --> 00:00:04,09 b=+is a computer language which is focused 3 00:00:04,09 --> 00:00:07,01 p!on a very narrow problem set. 4 00:00:07,01 --> 00:00:09,08 This is unlike general purposes languages like Kotlin, 5 00:00:09,08 --> 00:00:13,01 which can solve a wide variety of problems. 6 00:00:13,01 --> 00:00:15,06 We are going to create a very small DSL 7 00:00:15,06 --> 00:00:22,07 to help us unit test Kotlin code. 8 00:00:22,07 --> 00:00:25,07 Let's create a new file. 9 00:00:25,07 --> 00:00:27,06 Right-click on comm.tekadept.demo 10 00:00:27,06 --> 00:00:29,09 in the project window. 11 00:00:29,09 --> 00:00:33,07 Choose New > Kotlin File/Class. 12 00:00:33,07 --> 00:00:36,01 Name the file DSL, 13 00:00:36,01 --> 00:00:43,03 and add a package to the top. 14 00:00:43,03 --> 00:00:46,09 While traditional unit test frameworks use assert, 15 00:00:46,09 --> 00:00:50,07 many modern ones strive to make the test readable. 16 00:00:50,07 --> 00:00:53,09 So instead of having assert or assertTrue, 17 00:00:53,09 --> 00:00:56,06 Kotlin has many features which can help us 18 00:00:56,06 --> 00:00:59,08 j!7to achieve a more English-like sentence structure. 19 00:00:59,08 --> 00:01:10,08 We begin by defining an interface named matcher. 20 00:01:10,08 --> 00:01:13,02 +and this is going to be a generic type, 21 00:01:13,02 --> 00:01:16,06 so we'll give it a T. 22 00:01:16,06 --> 00:01:21,09 Inside of here we're going to have a function called test, 23 00:01:21,09 --> 00:01:25,06 and we're going to say lhs for left-hand side, 24 00:01:25,06 --> 00:01:28,06 and this is also going to be of type T. 25 00:01:28,06 --> 00:01:33,02 And it's going to return a type Unit, and Unit, once again, 26 00:01:33,02 --> 00:01:37,08 is Kotlin's version of a Java void. ! 27 00:01:37,08 --> 00:01:43,02 And then we're going to have an infix function or. " 28 00:01:43,02 --> 00:01:45,08 Or is the name of the function. # 29 00:01:45,08 --> 00:01:52,03 And then we're going to says other: Matcher, $ 30 00:01:52,03 --> 00:01:53,09 and this, again, is a generic, % 31 00:01:53,09 --> 00:01:57,00 so it's going to be of type T. & 32 00:01:57,00 --> 00:02:04,05 And it will return a Matcher of type T. ' 33 00:02:04,05 --> 00:02:09,03 And we'll have equals object, ( 34 00:02:09,03 --> 00:02:14,05 and another Matcher of type T. ) 35 00:02:14,05 --> 00:02:18,01 Now, all of this is going to be overrideable. * 36 00:02:18,01 --> 00:02:27,08 So we say override fun test(lhs), type T, + 37 00:02:27,08 --> 00:02:30,07 and then, because we're creating a unit test, , 38 00:02:30,07 --> 00:02:32,09 exceptions can occur while the test is running. - 39 00:02:32,09 --> 00:02:34,01 Z(So we want to be able to trap those, . 40 00:02:34,01 --> 00:02:38,07 and we want to trap those in an exception. / 41 00:02:38,07 --> 00:02:46,08 So we're going to say try this@Matcher.test, 0 42 00:02:46,08 --> 00:02:52,02 b7and the parameter we're going to give test is lhs. 1 43 00:02:52,02 --> 00:02:55,07 If something goes wrong, we're going to catch it, 2 44 00:02:55,07 --> 00:02:59,02 and we're going to say e:, 3 45 00:02:59,02 --> 00:03:05,02 and this would be a RuntimeException, 4 46 00:03:05,02 --> 00:03:12,02 and we'll say other.Test(lhs). 5 47 00:03:12,02 --> 00:03:17,06 So this is our interface for our unit test framework. 6 48 00:03:17,06 --> 00:03:21,01 Next, we define two versions of a should method. 7 49 00:03:21,01 --> 00:03:22,01 Again, what we're trying to do 8 50 00:03:22,01 --> 00:03:24,08 is make things read like English. 9 51 00:03:24,08 --> 00:03:26,04 y One works with single types, : 52 00:03:26,04 --> 00:03:28,06 and the other works on collections. ; 53 00:03:28,06 --> 00:03:31,03 |,Both are going to use the infix keyword. < 54 00:03:31,03 --> 00:03:35,04 AIt allows us to move the function in between its parameters. = 55 00:03:35,04 --> 00:03:37,05 This is similar to the what the plus operator > 56 00:03:37,05 --> 00:03:42,00 sits between its two operands. ? 57 00:03:42,00 --> 00:03:45,04 R$So again, we say infix function. @ 58 00:03:45,04 --> 00:03:51,01 a" It's going to be of type T, A 59 00:03:51,01 --> 00:03:55,07 and we're going to say should, and again, B 60 00:03:55,07 --> 00:03:58,05 our parameter here is going to be called matcher, C 61 00:03:58,05 --> 00:04:04,04 and it's going to be of type Matcher of T. D 62 00:04:04,04 --> 00:04:10,05 Open curly brace, matcher.test, E 63 00:04:10,05 --> 00:04:17,07 and what matcher's going to test is the current object. F 64 00:04:17,07 --> 00:04:22,02 And that right there is the should for a single object. H 65 00:04:22,02 --> 00:04:24,09 Now, we'll do the one for our collection. I 66 00:04:24,09 --> 00:04:32,00 Infix function of T. J 67 00:04:32,00 --> 00:04:40,05 Now we're going to have a Collection of T, K 68 00:04:40,05 --> 00:04:47,00 Bdot, should and the parameter is going to be fn for function, L 69 00:04:47,00 --> 00:04:53,04 CollectionMatchers, and that's Matchers with an S. M 70 00:04:53,04 --> 00:04:54,05 It's going to be red right now N 71 00:04:54,05 --> 00:04:59,09 because we haven't actually created that class yet, of T, O 72 00:04:59,09 --> 00:05:07,05 and then we're going to say dot, open, close, paren, P 73 00:05:07,05 --> 00:05:11,00 and any time you see the open, close paren and an arrow, Q 74 00:05:11,00 --> 00:05:16,01 that's indicating that we're going to pass it a function. R 75 00:05:16,01 --> 00:05:21,02 And we're going to say val matchers equals S 76 00:05:21,02 --> 00:05:30,04 CollectionMatchers, and again, we're going to pass it this. T 77 00:05:30,04 --> 00:05:36,06 And the final part for this function is matchers.fn, U 78 00:05:36,06 --> 00:05:42,04 and we'll call the function that got passed into this. V 79 00:05:42,04 --> 00:05:47,00 And then we create a CollectionMatchers class. W 80 00:05:47,00 --> 00:05:49,01 which comes after the word should. Y 81 00:05:49,01 --> 00:05:50,09 So it'll have something like should contain, Z 82 00:05:50,09 --> 00:05:55,02 should not contain, should have a size less than this. [ 83 00:05:55,02 --> 00:06:05,02 We'll say class CollectionMatchers \ 84 00:06:05,02 --> 00:06:13,00 val collection: Collection). 85 00:06:13,00 --> 00:06:14,00 I have a misspelling there. ^ 86 00:06:14,00 --> 00:06:18,06 Let's fix that. _ 87 00:06:18,06 --> 00:06:22,08 DNow, this class is going to contain all our different matchers. ` 88 00:06:22,08 --> 00:06:30,02 So the first one's going to be fun contains, a 89 00:06:30,02 --> 00:06:35,03 and we're going to call this rhs for right-hand side of T, b 90 00:06:35,03 --> 00:06:38,09 and it's going to return a type of Unit. c 91 00:06:38,09 --> 00:06:44,05 and we're going to say if not, check the collection d 92 00:06:44,05 --> 00:06:47,02 to see if it passes the test or not, e 93 00:06:47,02 --> 00:06:54,06 contains(rhs). f 94 00:06:54,06 --> 00:06:58,03 If it doesn't contain the item we're looking for, g 95 00:06:58,03 --> 00:07:04,05 we're going to throw a Runtime exception. h 96 00:07:04,05 --> 00:07:11,01 And give the user a useful message. i 97 00:07:11,01 --> 00:07:12,06 Let's do one more. j 98 00:07:12,06 --> 00:07:15,00 Let's do the opposite one. k 99 00:07:15,00 --> 00:07:20,07 Fun notContain, l 100 00:07:20,07 --> 00:07:23,07 and again, right-hand side. m 101 00:07:23,07 --> 00:07:27,02 The type is T. n 102 00:07:27,02 --> 00:07:28,08 Returns a Unit. o 103 00:07:28,08 --> 00:07:31,09 Open curly brace, close curly brace, p 104 00:07:31,09 --> 00:07:34,06 Aand this one looks very similar to the one that we just did, q 105 00:07:34,06 --> 00:07:40,09 except it's going to be if the collection contains r 106 00:07:40,09 --> 00:07:44,08 right-hand side, s 107 00:07:44,08 --> 00:07:50,08 throw a Runtime exception. t 108 00:07:50,08 --> 00:07:58,09 Collection should not contain. u 109 00:07:58,09 --> 00:08:02,07 And our final thing, we'll do one last matcher, v 110 00:08:02,07 --> 00:08:03,06 Vand this is going to be w 111 00:08:03,06 --> 00:08:05,06 just a slightly different kind of matcher. x 112 00:08:05,06 --> 00:08:15,00 And we're going to say, haveSizeLessThan, y 113 00:08:15,00 --> 00:08:17,03 and this one's going to be an integer. z 114 00:08:17,03 --> 00:08:22,05 It's going to return a type of Unit, { 115 00:08:22,05 --> 00:08:35,04 and if(collection.size >= size), | 116 00:08:35,04 --> 00:08:43,08 then we're going to throw a Runtime exception. } 117 00:08:43,08 --> 00:08:54,08 Collection should have size less than $size.  118 00:08:54,08 --> 00:08:58,08 V>3Okay, so that pretty much describes our class. 119 00:08:58,08 --> 00:09:01,03 ~@,Okay, looks like we have a little error. 120 00:09:01,03 --> 00:09:04,05 We're missing an R right here. 121 00:09:04,05 --> 00:09:07,09 And so we go ahead and add that R to matchers. 122 00:09:07,09 --> 00:09:10,07 FAnd now, all we want to do, 123 00:09:10,07 --> 00:09:17,00 G2let's create a function to hold our unitTest. 124 00:09:17,00 --> 00:09:28,00 And we're going to say val listOfNames = listOf, 125 00:09:28,00 --> 00:09:30,00 R-and we'll come up with a couple of names. 126 00:09:30,00 --> 00:09:40,02 SApril, May, and June. 127 00:09:40,02 --> 00:09:44,03 Y 00:09:50,03 So we'll say listOfNames should. 129 00:09:50,03 --> 00:09:54,06 This time, we have to put a function literal, 130 00:09:54,06 --> 00:09:58,01 b&and we're going to say notContain, 131 00:09:58,01 --> 00:10:02,05 qd:and for whatever reason, it shouldn't contain Portia. 132 00:10:02,05 --> 00:10:04,08 If the list of names contain Portia, 133 00:10:04,08 --> 00:10:06,08 Kh'a Runtime exception will be thrown. 134 00:10:06,08 --> 00:10:08,08 x@i-Take note of the way that the code reads, 135 00:10:08,08 --> 00:10:10,09 qj$almost like an English sentence. 136 00:10:10,09 --> 00:10:12,04 With a bit more hard work, 137 00:10:12,04 --> 00:10:14,09 we can improve its grammar even more.