trackler 2.0.6.34 → 2.0.6.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/common/exercises/change/canonical-data.json +6 -0
- data/common/exercises/luhn/canonical-data.json +35 -0
- data/common/exercises/rotational-cipher/cannonical-data.json +70 -0
- data/common/exercises/rotational-cipher/description.md +30 -0
- data/common/exercises/rotational-cipher/metadata.yml +4 -0
- data/common/exercises/run-length-encoding/description.md +7 -1
- data/lib/trackler/version.rb +1 -1
- data/tracks/elixir/exercises/run-length-encoding/example.exs +15 -4
- data/tracks/elixir/exercises/run-length-encoding/rle.exs +2 -2
- data/tracks/elixir/exercises/run-length-encoding/rle_test.exs +28 -11
- data/tracks/go/exercises/bob/bob_test.go +4 -1
- data/tracks/go/exercises/bracket-push/bracket_push_test.go +5 -2
- data/tracks/go/exercises/circular-buffer/circular_buffer_test.go +6 -6
- data/tracks/go/exercises/clock/clock_test.go +4 -1
- data/tracks/go/exercises/connect/connect_test.go +4 -1
- data/tracks/go/exercises/crypto-square/crypto_square_test.go +5 -2
- data/tracks/go/exercises/luhn/luhn_test.go +1 -1
- data/tracks/javascript/exercises/leap/leap.js +1 -1
- data/tracks/julia/.travis.yml +0 -2
- data/tracks/julia/config.json +41 -0
- data/tracks/julia/exercises/custom-set/HINTS.md +1 -0
- data/tracks/julia/exercises/custom-set/custom-set.jl +0 -0
- data/tracks/julia/exercises/custom-set/example.jl +51 -0
- data/tracks/julia/exercises/custom-set/runtests.jl +200 -0
- data/tracks/julia/exercises/isogram/example.jl +11 -0
- data/tracks/julia/exercises/isogram/isogram.jl +3 -0
- data/tracks/julia/exercises/isogram/runtests.jl +35 -0
- data/tracks/julia/exercises/luhn/example.jl +16 -0
- data/tracks/julia/exercises/luhn/luhn.jl +0 -0
- data/tracks/julia/exercises/luhn/runtests.jl +31 -0
- data/tracks/julia/exercises/nucleotide-count/example.jl +8 -0
- data/tracks/julia/exercises/nucleotide-count/nucleotide-count.jl +3 -0
- data/tracks/julia/exercises/nucleotide-count/runtests.jl +19 -0
- data/tracks/kotlin/.travis.yml +9 -1
- data/tracks/kotlin/README.md +117 -11
- data/tracks/kotlin/bin/journey-test.sh +279 -0
- data/tracks/kotlin/docs/INSTALLATION.md +193 -97
- data/tracks/kotlin/docs/TESTS.md +72 -137
- data/tracks/kotlin/exercises/hello-world/GETTING_STARTED.md +50 -0
- data/tracks/kotlin/exercises/hello-world/TUTORIAL.md +693 -0
- data/tracks/kotlin/exercises/hello-world/src/example/kotlin/HelloWorld.kt +2 -30
- data/tracks/kotlin/exercises/hello-world/src/main/kotlin/HelloWorld.kt +2 -30
- data/tracks/kotlin/exercises/hello-world/src/test/kotlin/HelloWorldTest.kt +11 -19
- data/tracks/objective-c/circle.yml +1 -1
- data/tracks/perl6/exercises/atbash-cipher/{cipher.t → atbash-cipher.t} +0 -0
- data/tracks/perl6/exercises/linked-list/linked-list.t +0 -0
- data/tracks/perl6/exercises/phone-number/{phone.t → phone-number.t} +0 -0
- data/tracks/perl6/exercises/rna-transcription/{rna_transcription.t → rna-transcription.t} +0 -0
- data/tracks/perl6/exercises/robot-name/{robot.t → robot-name.t} +0 -0
- data/tracks/perl6/exercises/scrabble-score/{scrabble_score.t → scrabble-score.t} +0 -0
- data/tracks/perl6/exercises/word-count/{word_count.t → word-count.t} +0 -0
- data/tracks/pony/config.json +9 -0
- data/tracks/pony/exercises/pascals-triangle/example.pony +18 -0
- data/tracks/pony/exercises/pascals-triangle/test.pony +31 -0
- data/tracks/r/config.json +20 -0
- data/tracks/r/exercises/grains/example.R +16 -0
- data/tracks/r/exercises/grains/grains.R +7 -0
- data/tracks/r/exercises/grains/test_grains.R +49 -0
- data/tracks/r/exercises/phone-number/example.R +30 -0
- data/tracks/r/exercises/phone-number/phone-number.R +3 -0
- data/tracks/r/exercises/phone-number/test_phone-number.R +44 -0
- data/tracks/r/exercises/secret-handshake/example.R +26 -0
- data/tracks/r/exercises/secret-handshake/secret-handshake.R +3 -0
- data/tracks/r/exercises/secret-handshake/test_secret-handshake.R +52 -0
- data/tracks/r/exercises/sieve/example.R +16 -0
- data/tracks/r/exercises/sieve/sieve.R +3 -0
- data/tracks/r/exercises/sieve/test_sieve.R +37 -0
- data/tracks/rust/config.json +11 -0
- data/tracks/rust/exercises/rotational-cipher/Cargo.lock +4 -0
- data/tracks/rust/exercises/rotational-cipher/Cargo.toml +4 -0
- data/tracks/rust/exercises/rotational-cipher/example.rs +16 -0
- data/tracks/rust/exercises/rotational-cipher/tests/rotational-cipher.rs +61 -0
- data/tracks/swift/circle.yml +6 -0
- metadata +46 -8
@@ -0,0 +1,50 @@
|
|
1
|
+
----
|
2
|
+
# Quick Start Guide
|
3
|
+
|
4
|
+
This guide picks-up where [Running the Tests (in Kotlin)](http://exercism.io/languages/kotlin/tests)
|
5
|
+
left off. If you haven't reviewed those instructions, do so now.
|
6
|
+
|
7
|
+
Need more information? A **step-by-step tutorial** is available in this directory at TUTORIAL.md or you can read
|
8
|
+
the [HTML version](https://github.com/exercism/xkotlin/blob/master/exercises/hello-world/TUTORIAL.md).
|
9
|
+
|
10
|
+
The following instructions work equally well on Windows, Mac OS X and Linux.
|
11
|
+
|
12
|
+
## Solve "Hello World"
|
13
|
+
|
14
|
+
Try writing a solution that passes one test at a time, running Gradle each time:
|
15
|
+
|
16
|
+
|
17
|
+
```
|
18
|
+
$ gradle test
|
19
|
+
```
|
20
|
+
|
21
|
+
## Iterate through the tests
|
22
|
+
|
23
|
+
After your first test passes, remove the `@Ignore` from the next test, and iterate on your solution,
|
24
|
+
testing after each change.
|
25
|
+
|
26
|
+
## All the tests pass? Submit your solution!
|
27
|
+
|
28
|
+
With a working solution that we've reviewed, we're ready to submit it to
|
29
|
+
exercism.io.
|
30
|
+
|
31
|
+
```
|
32
|
+
$ exercism submit src/main/kotlin/HelloWorld.kt
|
33
|
+
```
|
34
|
+
|
35
|
+
## Next Steps
|
36
|
+
|
37
|
+
From here, there are a number of paths you can take.
|
38
|
+
|
39
|
+
1. Move on to the next exercise
|
40
|
+
2. Review (and comment on) others' submissions to this exercise
|
41
|
+
3. Submit another iteration
|
42
|
+
4. Contribute to Exercism
|
43
|
+
|
44
|
+
|
45
|
+
We sincerely hope you learn and enjoy being part of this community. If at any time you need assistance
|
46
|
+
do not hesitate to ask for help:
|
47
|
+
|
48
|
+
http://exercism.io/languages/kotlin/help
|
49
|
+
|
50
|
+
Cheers!
|
@@ -0,0 +1,693 @@
|
|
1
|
+
NOTE: You can also view the HTML version of this file here:
|
2
|
+
https://github.com/exercism/xkotlin/blob/master/exercises/hello-world/TUTORIAL.md
|
3
|
+
|
4
|
+
* [Solving "Hello, World!"](#solving-hello-world)
|
5
|
+
* [Reading Gradle output](#reading-gradle-output)
|
6
|
+
* [Fixing the first failing test](#fixing-the-first-failing-test)
|
7
|
+
* [Enabling and fixing the second test](#enabling-and-fixing-the-second-test)
|
8
|
+
* [Enabling and fixing the third test](#enabling-and-fixing-the-third-test)
|
9
|
+
* [Enabling the last test](#enabling-the-last-test)
|
10
|
+
* [Refactoring](#refactoring)
|
11
|
+
* [Submitting your first iteration](#submitting-your-first-iteration)
|
12
|
+
* [Next Steps](#next-steps)
|
13
|
+
* [Review (and comment on) others' submissions to this exercise](#review-and-comment-on-others-submissions-to-this-exercise)
|
14
|
+
* [Extend an exercise](#extend-an-exercise)
|
15
|
+
* [Contribute to Exercism](#contribute-to-exercism)
|
16
|
+
|
17
|
+
----
|
18
|
+
|
19
|
+
# Solving "Hello, World!"
|
20
|
+
|
21
|
+
Welcome to the first exercise on the Kotlin track!
|
22
|
+
|
23
|
+
This is a step-by-step guide to solving this exercise.
|
24
|
+
|
25
|
+
Each exercise comes with a set of tests. The first pass through the
|
26
|
+
exercise is about getting all of the tests to pass, one at a time.
|
27
|
+
|
28
|
+
If you have not installed the Java Development Kit and Gradle, you must do
|
29
|
+
so now. For help with this, see: http://exercism.io/languages/kotlin/installing
|
30
|
+
|
31
|
+
----
|
32
|
+
|
33
|
+
This guide picks-up where [Running the Tests (in Kotlin)](http://exercism.io/languages/kotlin/tests)
|
34
|
+
left off. If you haven't reviewed those instructions, do so now.
|
35
|
+
|
36
|
+
The following instructions work equally well on Windows, Mac OS X and Linux.
|
37
|
+
|
38
|
+
## Reading Gradle output
|
39
|
+
|
40
|
+
Use Gradle to run the tests:
|
41
|
+
|
42
|
+
```
|
43
|
+
$ gradle test
|
44
|
+
```
|
45
|
+
|
46
|
+
This command does a lot and displays a bunch of stuff. Let's break it down...
|
47
|
+
|
48
|
+
```
|
49
|
+
:compileKotlin
|
50
|
+
w: /Users/jtigger/exercism/exercises/kotlin/hello-world/src/main/kotlin/HelloWorld.kt: (1, 11): Parameter 'name' is never used
|
51
|
+
:compileJava UP-TO-DATE
|
52
|
+
:copyMainKotlinClasses
|
53
|
+
:processResources UP-TO-DATE
|
54
|
+
:classes UP-TO-DATE
|
55
|
+
```
|
56
|
+
|
57
|
+
Each line that begins with a colon (like `:compileKotlin`) is Gradle telling
|
58
|
+
us that it's starting that task. The first five tasks are about compiling
|
59
|
+
the source code of our *solution*. We've done you a favor and included just
|
60
|
+
enough code for the solution that it compiles.
|
61
|
+
|
62
|
+
When a task is successful, it generally does not output anything. This is
|
63
|
+
why `:copyMainKotlinClasses` does not produce any additional output.
|
64
|
+
|
65
|
+
A task may succeed but warn of a potential problem. This is what we see from
|
66
|
+
`:compileKotlin`. The Kotlin compiler tells us that on line 1, 11 characters in
|
67
|
+
of the `HelloWorld.kt` file, there is a parameter called `name` that was
|
68
|
+
declared but never used. Usually, warnings _are_ helpful and should be heeded.
|
69
|
+
We'll address this warning soon enough, but we're okay for now.
|
70
|
+
|
71
|
+
The next five tasks are about compiling source code of the *tests*.
|
72
|
+
|
73
|
+
```
|
74
|
+
:compileTestKotlin
|
75
|
+
:compileTestJava UP-TO-DATE
|
76
|
+
:copyTestKotlinClasses
|
77
|
+
:processTestResources UP-TO-DATE
|
78
|
+
:testClasses UP-TO-DATE
|
79
|
+
```
|
80
|
+
|
81
|
+
... with both sets of source code successfully compiled, Gradle turns to
|
82
|
+
running the task you asked it to: executing the tests against the solution.
|
83
|
+
|
84
|
+
```
|
85
|
+
:test
|
86
|
+
|
87
|
+
HelloWorldTest > helloSampleName SKIPPED
|
88
|
+
|
89
|
+
HelloWorldTest > helloBlankName SKIPPED
|
90
|
+
|
91
|
+
HelloWorldTest > helloNoName FAILED
|
92
|
+
org.junit.ComparisonFailure: expected:<[Hello, World!]> but was:<[]>
|
93
|
+
at org.junit.Assert.assertEquals(Assert.java:115)
|
94
|
+
at org.junit.Assert.assertEquals(Assert.java:144)
|
95
|
+
at HelloWorldTest.helloNoName(HelloWorldTest.kt:10)
|
96
|
+
|
97
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
98
|
+
|
99
|
+
4 tests completed, 1 failed, 3 skipped
|
100
|
+
:test FAILED
|
101
|
+
|
102
|
+
FAILURE: Build failed with an exception.
|
103
|
+
|
104
|
+
* What went wrong:
|
105
|
+
Execution failed for task ':test'.
|
106
|
+
> There were failing tests. See the report at: file:///Users/jtigger/exercism/exercises/kotlin/hello-world/build/reports/tests/index.html
|
107
|
+
|
108
|
+
* Try:
|
109
|
+
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
|
110
|
+
|
111
|
+
BUILD FAILED
|
112
|
+
|
113
|
+
Total time: 7.473 secs
|
114
|
+
```
|
115
|
+
|
116
|
+
Seeing the word "fail" NINE TIMES might give you the impression you've done
|
117
|
+
something horribly wrong! You haven't. It's a whole lot of noise over
|
118
|
+
a single test not passing.
|
119
|
+
|
120
|
+
Let's focus in on the important bits:
|
121
|
+
|
122
|
+
```
|
123
|
+
HelloWorldTest > helloNoName FAILED
|
124
|
+
org.junit.ComparisonFailure: expected:<[Hello, World!]> but was:<[]>
|
125
|
+
```
|
126
|
+
|
127
|
+
...is read: "Within the test class named `HelloWorldTest`, the test method
|
128
|
+
`helloNoName` did not pass because the solution did not satisfy an
|
129
|
+
assertion. Apparently, we expected to see the string 'Hello, World!' but
|
130
|
+
a blank string was returned instead.
|
131
|
+
|
132
|
+
The last line of the stack trace tells us exactly where this unsatisfied
|
133
|
+
assertion lives:
|
134
|
+
|
135
|
+
```
|
136
|
+
at HelloWorldTest.helloNoName(HelloWorldTest.kt:10)
|
137
|
+
```
|
138
|
+
|
139
|
+
Looks like the scene of the crime is on line 10 in the test file.
|
140
|
+
|
141
|
+
Knowing these two facts,
|
142
|
+
|
143
|
+
1. the return value was not what was expected, and
|
144
|
+
2. the failure was on line 10 of the test,
|
145
|
+
|
146
|
+
we can turn this failure into success.
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
## Fixing the first failing test
|
151
|
+
|
152
|
+
In your favorite text editor, open `src/test/kotlin/HelloWorldTest.kt`
|
153
|
+
and go to line 10.
|
154
|
+
|
155
|
+
```kotlin
|
156
|
+
assertEquals("Hello, World!", hello(""));
|
157
|
+
```
|
158
|
+
|
159
|
+
The test is expecting that `hello()`, when given an empty string (`""`),
|
160
|
+
returns "Hello, World!". Instead, `hello()` is returning `""`.
|
161
|
+
Let's fix that.
|
162
|
+
|
163
|
+
Open `src/main/kotlin/HelloWorld.kt`.
|
164
|
+
|
165
|
+
```kotlin
|
166
|
+
fun hello(name: String = ""): String {
|
167
|
+
return ""
|
168
|
+
}
|
169
|
+
```
|
170
|
+
|
171
|
+
Let's change that to return the expected string:
|
172
|
+
|
173
|
+
```kotlin
|
174
|
+
fun hello(name: String = ""): String {
|
175
|
+
return "Hello, World!"
|
176
|
+
}
|
177
|
+
```
|
178
|
+
|
179
|
+
Save the file and run the tests again:
|
180
|
+
|
181
|
+
```
|
182
|
+
$ gradle test
|
183
|
+
:compileKotlin
|
184
|
+
w: /Users/jtigger/exercism/exercises/kotlin/hello-world/src/main/kotlin/HelloWorld.kt: (1, 11): Parameter 'name' is never used
|
185
|
+
:compileJava UP-TO-DATE
|
186
|
+
:copyMainKotlinClasses
|
187
|
+
:processResources UP-TO-DATE
|
188
|
+
:classes UP-TO-DATE
|
189
|
+
:compileTestKotlin
|
190
|
+
:compileTestJava UP-TO-DATE
|
191
|
+
:copyTestKotlinClasses UP-TO-DATE
|
192
|
+
:processTestResources UP-TO-DATE
|
193
|
+
:testClasses UP-TO-DATE
|
194
|
+
:test
|
195
|
+
|
196
|
+
HelloWorldTest > helloSampleName SKIPPED
|
197
|
+
|
198
|
+
HelloWorldTest > helloBlankName SKIPPED
|
199
|
+
|
200
|
+
HelloWorldTest > helloNoName PASSED
|
201
|
+
|
202
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
203
|
+
|
204
|
+
BUILD SUCCESSFUL
|
205
|
+
|
206
|
+
Total time: 7.318 secs
|
207
|
+
```
|
208
|
+
|
209
|
+
"BUILD SUCCESSFUL"! Woohoo! :) You can see that `helloNoName()` test is
|
210
|
+
now passing.
|
211
|
+
|
212
|
+
We still see the warning about `name` not being used; we'll get to that
|
213
|
+
next.
|
214
|
+
|
215
|
+
With one win under our belt, we can turn our focus to some other messages
|
216
|
+
that we've been ignoring: the lines ending in "`SKIPPED`".
|
217
|
+
|
218
|
+
Each test suite contains a series of tests, all of which have been marked
|
219
|
+
to be skipped/ignored except the first one. We did this to help you focus
|
220
|
+
on getting one test running at a time.
|
221
|
+
|
222
|
+
Let's tackle the next test...
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
## Enabling and fixing the second test
|
227
|
+
|
228
|
+
Right now, that second test is being skipped/ignored. Let's enable it.
|
229
|
+
|
230
|
+
(Re)open `src/test/kotlin/HelloWorldTest.kt` and find the second test:
|
231
|
+
|
232
|
+
```kotlin
|
233
|
+
@Test
|
234
|
+
@Ignore
|
235
|
+
fun helloSampleName() {
|
236
|
+
assertEquals("Hello, Alice!", hello("Alice"))
|
237
|
+
}
|
238
|
+
```
|
239
|
+
|
240
|
+
When the JUnit test runner sees that `@Ignore` annotation on the test
|
241
|
+
method, it knows to skip over that test. Remove that line:
|
242
|
+
|
243
|
+
```kotlin
|
244
|
+
@Test
|
245
|
+
fun helloSampleName() {
|
246
|
+
assertEquals("Hello, Alice!", hello("Alice"))
|
247
|
+
}
|
248
|
+
```
|
249
|
+
|
250
|
+
Now, when you run the tests, both tests run:
|
251
|
+
|
252
|
+
```
|
253
|
+
$ gradle test
|
254
|
+
:test
|
255
|
+
|
256
|
+
HelloWorldTest > helloSampleName FAILED
|
257
|
+
org.junit.ComparisonFailure: expected:<Hello, [Alice]!> but was:<Hello, [World]!>
|
258
|
+
at org.junit.Assert.assertEquals(Assert.java:115)
|
259
|
+
at org.junit.Assert.assertEquals(Assert.java:144)
|
260
|
+
at HelloWorldTest.helloSampleName(HelloWorldTest.kt:15)
|
261
|
+
|
262
|
+
HelloWorldTest > helloBlankName SKIPPED
|
263
|
+
|
264
|
+
HelloWorldTest > helloNoName PASSED
|
265
|
+
|
266
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
267
|
+
|
268
|
+
4 tests completed, 1 failed, 2 skipped
|
269
|
+
```
|
270
|
+
|
271
|
+
The first test, `helloNoName()` continues to pass. We see that
|
272
|
+
`helloSampleName` -- the test we just un-`@Ignore`'d -- is now running and
|
273
|
+
failing. Yay, failing test! In fact, the "failure" message is just
|
274
|
+
describing the difference between what the program does now and what it
|
275
|
+
should do for us to call it "done."
|
276
|
+
|
277
|
+
Right now, we've hardcoded the greeting. Enabling this second test has
|
278
|
+
unleashed a new expectation: that our program incorporate a name given
|
279
|
+
into that greeting. When given the name "`Alice`", that's who should be
|
280
|
+
greeted instead of "`World`".
|
281
|
+
|
282
|
+
(Re)open `src/main/kotlin/HelloWorld.kt`.
|
283
|
+
|
284
|
+
```kotlin
|
285
|
+
fun hello(name: String = ""): String {
|
286
|
+
return "Hello, World!"
|
287
|
+
}
|
288
|
+
```
|
289
|
+
|
290
|
+
While `hello()` does accept a reference to a string named `name`, it is not
|
291
|
+
using it in the output. Let's change that:
|
292
|
+
|
293
|
+
|
294
|
+
```kotlin
|
295
|
+
fun hello(name: String = ""): String {
|
296
|
+
return "Hello, $name!"
|
297
|
+
}
|
298
|
+
```
|
299
|
+
|
300
|
+
_(Kotlin allows you to embed expressions within strings, a feature known as
|
301
|
+
string interpolation. For more about this feature, see
|
302
|
+
https://kotlinlang.org/docs/reference/basic-types.html#string-templates )_
|
303
|
+
|
304
|
+
... and rerun the tests ...
|
305
|
+
|
306
|
+
```
|
307
|
+
$ gradle test
|
308
|
+
:test
|
309
|
+
|
310
|
+
HelloWorldTest > helloSampleName PASSED
|
311
|
+
|
312
|
+
HelloWorldTest > helloBlankName SKIPPED
|
313
|
+
|
314
|
+
HelloWorldTest > helloNoName FAILED
|
315
|
+
org.junit.ComparisonFailure: expected:<Hello, [World]!> but was:<Hello, []!>
|
316
|
+
at org.junit.Assert.assertEquals(Assert.java:115)
|
317
|
+
at org.junit.Assert.assertEquals(Assert.java:144)
|
318
|
+
at HelloWorldTest.helloNoName(HelloWorldTest.kt:10)
|
319
|
+
|
320
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
321
|
+
|
322
|
+
4 tests completed, 1 failed, 2 skipped
|
323
|
+
```
|
324
|
+
|
325
|
+
Wait... didn't we just fix the test? Why is it failing? Take a closer look...
|
326
|
+
|
327
|
+
In fact, `helloSampleName()` *is* passing. It's just that at the same time,
|
328
|
+
we just inadvertently broke that first test: `helloNoName()`.
|
329
|
+
|
330
|
+
This is one tiny example of the benefit of maintaining a test suite: if we
|
331
|
+
use them to drive out our code, the second we break the program the tests
|
332
|
+
say so. Since we saw them passing just *before* our latest change,
|
333
|
+
whatever we *just* did most likely cause that regression.
|
334
|
+
|
335
|
+
Our latest change was making the greeting dependent on the name given. If
|
336
|
+
no name is given, our function defaults to an empty string. The intent is
|
337
|
+
that when `hello()` is called on no one in particular, our function greets
|
338
|
+
the whole world. Sound like a job for a default value!
|
339
|
+
|
340
|
+
`src/main/kotlin/HelloWorld.kt`:
|
341
|
+
```kotlin
|
342
|
+
fun hello(name: String = "World"): String {
|
343
|
+
return "Hello, $name!"
|
344
|
+
}
|
345
|
+
```
|
346
|
+
|
347
|
+
... and re-run the tests ...
|
348
|
+
|
349
|
+
```
|
350
|
+
$ gradle test
|
351
|
+
:compileKotlin
|
352
|
+
:compileJava UP-TO-DATE
|
353
|
+
:copyMainKotlinClasses
|
354
|
+
:processResources UP-TO-DATE
|
355
|
+
:classes UP-TO-DATE
|
356
|
+
:compileTestKotlin
|
357
|
+
:compileTestJava UP-TO-DATE
|
358
|
+
:copyTestKotlinClasses
|
359
|
+
:processTestResources UP-TO-DATE
|
360
|
+
:testClasses UP-TO-DATE
|
361
|
+
:test
|
362
|
+
|
363
|
+
HelloWorldTest > helloSampleName PASSED
|
364
|
+
|
365
|
+
HelloWorldTest > helloBlankName SKIPPED
|
366
|
+
|
367
|
+
HelloWorldTest > helloNoName PASSED
|
368
|
+
|
369
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
370
|
+
|
371
|
+
BUILD SUCCESSFUL
|
372
|
+
```
|
373
|
+
|
374
|
+
Excellent! Not only are both our tests passing, but that pesky warning
|
375
|
+
about not using `name` has faded into the distant past. We're now
|
376
|
+
(at least) three-fourth the way done. Just two more tests to go...
|
377
|
+
|
378
|
+
|
379
|
+
|
380
|
+
## Enabling and fixing the third test
|
381
|
+
|
382
|
+
(Re)open `src/test/kotlin/HelloWorldTest.kt` and find the penultimate test:
|
383
|
+
|
384
|
+
```kotlin
|
385
|
+
@Test
|
386
|
+
@Ignore
|
387
|
+
fun helloBlankName() {
|
388
|
+
assertEquals("Hello, World!", hello(" "))
|
389
|
+
}
|
390
|
+
```
|
391
|
+
|
392
|
+
In this test, we're trying to be tricky. It's one thing to omit a
|
393
|
+
parameter completely; it's a whole other situation when we provide a blank
|
394
|
+
string for a name. This test is telling us that we'd like to treat these
|
395
|
+
cases the same way.
|
396
|
+
|
397
|
+
... and remove it's `@Ignore` to enable it ...
|
398
|
+
|
399
|
+
```kotlin
|
400
|
+
@Test
|
401
|
+
fun helloBlankName() {
|
402
|
+
assertEquals("Hello, World!", hello(" "))
|
403
|
+
}
|
404
|
+
```
|
405
|
+
|
406
|
+
... and re-run the tests ...
|
407
|
+
|
408
|
+
```
|
409
|
+
$ gradle test
|
410
|
+
:test
|
411
|
+
|
412
|
+
HelloWorldTest > helloSampleName PASSED
|
413
|
+
|
414
|
+
HelloWorldTest > helloBlankName FAILED
|
415
|
+
org.junit.ComparisonFailure: expected:<Hello, [World]!> but was:<Hello, [ ]!>
|
416
|
+
at org.junit.Assert.assertEquals(Assert.java:115)
|
417
|
+
at org.junit.Assert.assertEquals(Assert.java:144)
|
418
|
+
at HelloWorldTest.helloBlankName(HelloWorldTest.kt:20)
|
419
|
+
|
420
|
+
HelloWorldTest > helloNoName PASSED
|
421
|
+
|
422
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
423
|
+
|
424
|
+
4 tests completed, 1 failed, 1 skipped
|
425
|
+
```
|
426
|
+
|
427
|
+
Since `" "` is an actual value, Kotlin does _not_ substitute in the
|
428
|
+
default value.
|
429
|
+
|
430
|
+
(Re)open `src/main/kotlin/HelloWorld.kt`.
|
431
|
+
|
432
|
+
```kotlin
|
433
|
+
fun hello(name: String = "World"): String {
|
434
|
+
return "Hello, $name!"
|
435
|
+
}
|
436
|
+
```
|
437
|
+
|
438
|
+
One way to handle this case is to check to see if `name` is blank. Let's
|
439
|
+
do that:
|
440
|
+
|
441
|
+
|
442
|
+
```kotlin
|
443
|
+
fun hello(name: String = "World"): String {
|
444
|
+
return "Hello, ${if (name.isBlank()) "World" else name}!"
|
445
|
+
}
|
446
|
+
```
|
447
|
+
|
448
|
+
As you can see, string templates can contain not just references to
|
449
|
+
variables, but entire expressions! This is appropriate in a case like this
|
450
|
+
where we want to apply a simple condition to a value.
|
451
|
+
|
452
|
+
... and rerun the tests ...
|
453
|
+
|
454
|
+
```
|
455
|
+
$ gradle test
|
456
|
+
:test
|
457
|
+
|
458
|
+
HelloWorldTest > helloSampleName PASSED
|
459
|
+
|
460
|
+
HelloWorldTest > helloBlankName PASSED
|
461
|
+
|
462
|
+
HelloWorldTest > helloNoName PASSED
|
463
|
+
|
464
|
+
HelloWorldTest > helloAnotherSampleName SKIPPED
|
465
|
+
|
466
|
+
BUILD SUCCESSFUL
|
467
|
+
```
|
468
|
+
|
469
|
+
We're almost there (perhaps closer than you think)! Just _one_ more test
|
470
|
+
to pass before we have a solution we can have real confidence in.
|
471
|
+
|
472
|
+
|
473
|
+
|
474
|
+
## Enabling the last test
|
475
|
+
|
476
|
+
(Re)open `src/test/kotlin/HelloWorldTest.kt` and find the last test:
|
477
|
+
|
478
|
+
```kotlin
|
479
|
+
@Test
|
480
|
+
@Ignore
|
481
|
+
fun helloAnotherSampleName() {
|
482
|
+
assertEquals("Hello, Bob!", hello("Bob"))
|
483
|
+
}
|
484
|
+
```
|
485
|
+
|
486
|
+
... and pop-off that `@Ignore` ...
|
487
|
+
|
488
|
+
```kotlin
|
489
|
+
@Test
|
490
|
+
fun helloAnotherSampleName() {
|
491
|
+
assertEquals("Hello, Bob!", hello("Bob"))
|
492
|
+
}
|
493
|
+
```
|
494
|
+
|
495
|
+
... then rerun the tests ...
|
496
|
+
|
497
|
+
```
|
498
|
+
:test
|
499
|
+
|
500
|
+
HelloWorldTest > helloSampleName PASSED
|
501
|
+
|
502
|
+
HelloWorldTest > helloBlankName PASSED
|
503
|
+
|
504
|
+
HelloWorldTest > helloNoName PASSED
|
505
|
+
|
506
|
+
HelloWorldTest > helloAnotherSampleName PASSED
|
507
|
+
|
508
|
+
BUILD SUCCESSFUL
|
509
|
+
```
|
510
|
+
|
511
|
+
Oh, hello! Turns out, the solution we put into place didn't just apply for
|
512
|
+
"`Alice`" but for "`Bob`" equally well. In this case, the test succeeded
|
513
|
+
with no additional code on our part.
|
514
|
+
|
515
|
+
Congratulations!
|
516
|
+
|
517
|
+
|
518
|
+
|
519
|
+
## Refactoring
|
520
|
+
|
521
|
+
Now that you've got all the tests passing, you might consider whether
|
522
|
+
the code is in the most readable/maintainable/efficient shape. What makes
|
523
|
+
for "good" design of software is a big topic. The pursuit of it underlies
|
524
|
+
much of what makes up the more valuable conversations on Exercism.
|
525
|
+
|
526
|
+
Kotlin is such a concise language and this exercise is so small, there is
|
527
|
+
not much room for us to make adjustments. Most would leave this code, as
|
528
|
+
is.
|
529
|
+
|
530
|
+
That said, we've taken such pains to illustrate two core parts of the
|
531
|
+
Test-Driven Development approach (i.e. "red", "green"), we'd be remiss if
|
532
|
+
we skipped the all important final part: "refactor".
|
533
|
+
|
534
|
+
More on TDD at http://www.jamesshore.com/Blog/Red-Green-Refactor.html.
|
535
|
+
|
536
|
+
The core responsibility of `hello()` is to produce a personalized greeting.
|
537
|
+
_How_ we determine whether or not a name is given (i.e. `name` is
|
538
|
+
effectively an empty string) is a lower-level detail.
|
539
|
+
|
540
|
+
```kotlin
|
541
|
+
fun hello(name: String = "World"): String {
|
542
|
+
return "Hello, ${if (name.isBlank()) "World" else name}!"
|
543
|
+
}
|
544
|
+
```
|
545
|
+
|
546
|
+
How would things read if we extracted that detail into a separate method?
|
547
|
+
|
548
|
+
```kotlin
|
549
|
+
fun hello(name: String = ""): String {
|
550
|
+
return "Hello, ${whom(name)}!"
|
551
|
+
}
|
552
|
+
|
553
|
+
private fun whom(name: String):String {
|
554
|
+
return if(name.isBlank()) "World" else name;
|
555
|
+
}
|
556
|
+
```
|
557
|
+
|
558
|
+
By extracting that logic into the `whom()` method, we've added a little
|
559
|
+
abstraction to our program — it's not as literal as it was before. Yet,
|
560
|
+
it allows us to defer _needing_ to understand _how_ the recipient of the
|
561
|
+
greeting is determined.
|
562
|
+
|
563
|
+
If we can assume that `whom()` just works, we don't have to
|
564
|
+
downshift in our head to those details. Instead, we can remain at the same
|
565
|
+
level of thinking: what's the greeting?
|
566
|
+
|
567
|
+
_(Yes, this is considerable more lines of code; again, not a move we'd likely
|
568
|
+
make typically. The takeaway is this: when you are "done" with an exercise
|
569
|
+
ask yourself, "can I adjust the shape of this code to better tell the
|
570
|
+
story of what's going on through its shape?")_
|
571
|
+
|
572
|
+
We made a bunch of changes, let's make sure we didn't break the program!
|
573
|
+
|
574
|
+
```
|
575
|
+
$ gradle test
|
576
|
+
:compileKotlin
|
577
|
+
:compileJava UP-TO-DATE
|
578
|
+
:copyMainKotlinClasses
|
579
|
+
:processResources UP-TO-DATE
|
580
|
+
:classes UP-TO-DATE
|
581
|
+
:compileTestKotlin
|
582
|
+
:compileTestJava UP-TO-DATE
|
583
|
+
:copyTestKotlinClasses UP-TO-DATE
|
584
|
+
:processTestResources UP-TO-DATE
|
585
|
+
:testClasses UP-TO-DATE
|
586
|
+
:test
|
587
|
+
|
588
|
+
HelloWorldTest > helloSampleName PASSED
|
589
|
+
|
590
|
+
HelloWorldTest > helloBlankName PASSED
|
591
|
+
|
592
|
+
HelloWorldTest > helloNoName PASSED
|
593
|
+
|
594
|
+
HelloWorldTest > helloAnotherSampleName PASSED
|
595
|
+
|
596
|
+
BUILD SUCCESSFUL
|
597
|
+
```
|
598
|
+
|
599
|
+
This illustrates another benefit of writing tests: you can make significant
|
600
|
+
changes to the structure of the program and very quickly restore your
|
601
|
+
confidence that the program still works. These tests are a far cry from a
|
602
|
+
"proof" of correctness, but well-written tests do a much better job of
|
603
|
+
(very quickly) giving us evidence that it is. Without them, we manually
|
604
|
+
run the program with different inputs and/or inspecting the code
|
605
|
+
line-by-line — time-consuming and error prone.
|
606
|
+
|
607
|
+
|
608
|
+
|
609
|
+
# Submitting your first iteration
|
610
|
+
|
611
|
+
With a working solution that we've reviewed, we're ready to submit it to
|
612
|
+
exercism.io.
|
613
|
+
|
614
|
+
```
|
615
|
+
$ exercism submit src/main/kotlin/HelloWorld.kt
|
616
|
+
```
|
617
|
+
|
618
|
+
|
619
|
+
|
620
|
+
# Next Steps
|
621
|
+
|
622
|
+
From here, there are a number of paths you can take.
|
623
|
+
|
624
|
+
|
625
|
+
## Move on to the next exercise
|
626
|
+
|
627
|
+
There are many more exercises you can practice with. Grab the next one!
|
628
|
+
|
629
|
+
```
|
630
|
+
$ exercism fetch kotlin
|
631
|
+
```
|
632
|
+
|
633
|
+
|
634
|
+
## Review (and comment on) others' submissions to this exercise
|
635
|
+
|
636
|
+
The heart of Exercism is the conversations about coding
|
637
|
+
practices. It's definitely fun to practice, but engaging with others
|
638
|
+
both in their attempts and your own is how you get feedback. That feedback
|
639
|
+
can help point out what you're doing well and where you might need to
|
640
|
+
improve.
|
641
|
+
|
642
|
+
Some submissions will be nearly identical to yours; others will be
|
643
|
+
completely different. Seeing both kinds can be instructive and interesting.
|
644
|
+
|
645
|
+
Note that you can only view submissions of others for exercises you have
|
646
|
+
completed yourself. This enriches the experience of reading others' code
|
647
|
+
because you'll have your own experience of trying to solve the problem.
|
648
|
+
|
649
|
+
Here's an up-to-date list of submissions on the Kotlin track:
|
650
|
+
|
651
|
+
http://exercism.io/tracks/kotlin/exercises
|
652
|
+
|
653
|
+
|
654
|
+
|
655
|
+
## Submit another iteration
|
656
|
+
|
657
|
+
You are also encouraged to consider additional "requirements" on a given
|
658
|
+
exercise.
|
659
|
+
|
660
|
+
For example, you could add a test or two that requires that the greeting
|
661
|
+
use the capitalized form on the person's name, regardless of the case they
|
662
|
+
used.
|
663
|
+
|
664
|
+
In that situation, you'd:
|
665
|
+
|
666
|
+
1. add a new test setting up that new expectation,
|
667
|
+
2. implement that in the code (the same process we just went through
|
668
|
+
together, above).
|
669
|
+
3. review your code for readability and refactor as you see fit.
|
670
|
+
|
671
|
+
Exercism practitioners who "play" with each exercise — over trying to go as
|
672
|
+
fast as they can through the stream of exercises — report deep rewards.
|
673
|
+
|
674
|
+
|
675
|
+
## Contribute to Exercism
|
676
|
+
|
677
|
+
The entire of Exercism is Open Source and is the labor of love for more
|
678
|
+
than 100 maintainers and many more contributors.
|
679
|
+
|
680
|
+
A starting point to jumping in can be found here:
|
681
|
+
|
682
|
+
https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md
|
683
|
+
|
684
|
+
|
685
|
+
----
|
686
|
+
|
687
|
+
Regardless of what you decide to do next, we sincerely hope you learn
|
688
|
+
and enjoy being part of this community. If at any time you need assistance
|
689
|
+
do not hesitate to ask for help:
|
690
|
+
|
691
|
+
http://exercism.io/languages/kotlin/help
|
692
|
+
|
693
|
+
Cheers!
|