diff --git a/exercises/hello-world/GETTING_STARTED.md b/exercises/hello-world/GETTING_STARTED.md index 4bfc62b74..b752295e9 100644 --- a/exercises/hello-world/GETTING_STARTED.md +++ b/exercises/hello-world/GETTING_STARTED.md @@ -1,500 +1,29 @@ -NOTE: You can also view the HTML version of this file here: -https://github.com/exercism/xjava/blob/master/exercises/hello-world/GETTING_STARTED.md - -* [Solving "Hello, World!"](#solving-hello-world) - * [Reading Gradle output](#reading-gradle-output) - * [Fixing the first failing test](#fixing-the-first-failing-test) - * [Enabling and fixing the second test](#enabling-and-fixing-the-second-test) - * [Enabling the last test](#enabling-the-last-test) - * [Refactoring](#refactoring) -* [Submitting your first iteration](#submitting-your-first-iteration) -* [Next Steps](#next-steps) - * [Review (and comment on) others' submissions to this exercise](#review-and-comment-on-others-submissions-to-this-exercise) - * [Extend an exercise](#extend-an-exercise) - ----- - -# Solving "Hello, World!" - -Welcome to the first exercise on the Java track! - -This is a step-by-step guide to solving this exercise. - -Each exercise comes with a set of tests. The first pass through the -exercise is about getting all of the tests to pass, one at a time. - -If you have not installed the Java Development Kit and Gradle, you must do -so now. For help with this, see: http://exercism.io/languages/java/installing - ---- +# Quick Start Guide This guide picks-up where [Running the Tests (in Java)](http://exercism.io/languages/java/tests) left off. If you haven't reviewed those instructions, do so now. -The following instructions work equally well on Windows, Mac OS X and Linux. - -## Reading Gradle output - -Use Gradle to run the tests: - -``` -$ gradle test -``` - -This command does a lot and displays a bunch of stuff. Let's break it down... - -``` -:compileJava -:processResources UP-TO-DATE -:classes -``` - -Each line that begins with a colon (like `:compileJava`) is Gradle telling -us that it's starting that task. The first three tasks are about compiling -the source code of our *solution*. We've done you a favor and included just -enough code for the solution that it compiles. - -When a task is successful, it generally does not output anything. This is -why `:compileJava` and `:classes` do not produce any additional output. -`:processResources` reports that it had nothing to do. - -So far, so good... - -The next three tasks are about compiling source code of the *tests*. - -``` -:compileTestJava -:processTestResources UP-TO-DATE -:testClasses -``` - -... with both sets of source code successfully compiled, Gradle turns to -running the task you asked it to: executing the tests against the solution. - -``` -:test - -HelloWorldTest > helloNoName FAILED - java.lang.AssertionError: expected: but was: - at org.junit.Assert.fail(Assert.java:93) - at org.junit.Assert.failNotEquals(Assert.java:647) - at org.junit.Assert.assertEquals(Assert.java:128) - at org.junit.Assert.assertEquals(Assert.java:147) - at HelloWorldTest.helloNoName(HelloWorldTest.java:10) - -HelloWorldTest > helloSampleName SKIPPED - -HelloWorldTest > helloAnotherSampleName SKIPPED - -3 tests completed, 1 failed, 2 skipped -:test FAILED - -FAILURE: Build failed with an exception. - -* What went wrong: -Execution failed for task ':test'. -> There were failing tests. See the report at: file:///Users/jtigger/projects/exercism/xjava/build/exercism/java/hello-world/build/reports/tests/index.html - -* Try: -Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. - -BUILD FAILED - -Total time: 6.716 secs -``` - -Seeing the word "fail" TEN TIMES might give you the impression you've done -something horribly wrong. You haven't. It's a whole lot of noise over -a single test not passing. - -Let's focus in on the important bits: - -``` -HelloWorldTest > helloNoName FAILED - java.lang.AssertionError: expected: but was: -``` - -...is read: "Within the test class named `HelloWorldTest`, the test method -`helloNoName` did not pass because the solution did not satisfy an -assertion. Apparently, we expected to see the string 'Hello, World!' but -the value `null` was returned instead. - -The last line of the stack trace tells us exactly where this unsatisfied -assertion lives: - -``` - at HelloWorldTest.helloNoName(HelloWorldTest.java:10) -``` - -Looks like the crime was discovered on line 10 in the test file. - -Knowing these two facts, - -1. the return value was not what was expected, and -2. the failure was on line 10 of the test, - -we can turn this failure into success. - - - -## Fixing the first failing test - -In your favorite text editor, open `src/test/java/HelloWorldTest.java` -and go to line 10. - -```java -assertEquals("Hello, World!", HelloWorld.hello("")); -``` - -The test is expecting that `hello()`, when given an empty string (`""`), -returns "Hello, World!". Instead, `hello()` is returning `null`. -Let's fix that. - -Open `src/main/java/HelloWorld.java`. - -```java -public class HelloWorld { - public static String hello(String name) { - return null; - } -} -``` - -Let's change that to return the expected string: - -```java -public class HelloWorld { - public static String hello(String name) { - return "Hello, World!"; - } -} -``` - -Save the file and run the tests again: - -``` -$ gradle test -:compileJava -:processResources UP-TO-DATE -:classes -:compileTestJava -:processTestResources UP-TO-DATE -:testClasses -:test - -HelloWorldTest > helloAnotherSampleName SKIPPED - -HelloWorldTest > helloNoName PASSED - -HelloWorldTest > helloSampleName SKIPPED - -BUILD SUCCESSFUL - -Total time: 4.523 secs -``` - -"BUILD SUCCESSFUL"! Woohoo! :) You can see that `helloNoName()` test is -now passing. - -With one win under our belt, we can turn our focus to some other messages -that we've been ignoring: the lines ending in "`SKIPPED`". - -Each test suite contains a series of tests, all of which have been marked -to be skipped/ignored except the first one. We did this to help you focus -on getting one test running at a time. - -Let's tackle the next test... - - - -## Enabling and fixing the second test - -Right now, that second test is being skipped/ignored. Let's enable it. - -(Re)open `src/test/java/HelloWorldTest.java` and find the second test: - -```java -... -@Test -@Ignore -public void helloSampleName() { - assertEquals("Hello, Alice!", HelloWorld.hello("Alice")); -} -... -``` - -When the JUnit test runner sees that `@Ignore` annotation on the test -method, it knows to skip over that test. Remove that line: - -```java -... -@Test -public void helloSampleName() { - assertEquals("Hello, Alice!", HelloWorld.hello("Alice")); -} -... -``` - -Now, when you run the tests, both tests run: - -```sh -$ gradle test -... -:test - -HelloWorldTest > helloNoName PASSED - -HelloWorldTest > helloSampleName FAILED - org.junit.ComparisonFailure: expected: but was: - at org.junit.Assert.assertEquals(Assert.java:125) - at org.junit.Assert.assertEquals(Assert.java:147) - at HelloWorldTest.helloSampleName(HelloWorldTest.java:16) - -HelloWorldTest > helloAnotherSampleName SKIPPED - -3 tests completed, 1 failed, 1 skipped -... -``` - -The first test, `helloNoName()` continues to pass. We see that -`helloSampleName` -- the test we just un-`@Ignore`'d -- is now running and -failing. Yay, failing test! In fact, the "failure" message is just -describing the difference between what the program does now and what it -should do for us to call it "done." +Need more information? A **step-by-step tutorial** is available in this directory at TUTORIAL.md or you can read +the [HTML version](https://github.com/exercism/xjava/blob/master/exercises/hello-world/TUTORIAL.md). -Right now, we've hardcoded the greeting. Enabling this second test has -unleashed a new expectation: that our program incorporate a name given -into that greeting. When given the name "`Alice`", that's who should be -greeted instead of "`World`". - -(Re)open `src/main/java/HelloWorld.java`. - -```java -public class HelloWorld { - public static String hello(String name) { - return "Hello, World!"; - } -} -``` - -While `hello()` does accept a reference to a string named `name`, it is not -using it in the output. Let's change that: - - -```java -public class HelloWorld { - public static String hello(String name) { - return "Hello, " + name + "!"; - } -} -``` - -... and rerun the tests ... - -``` -$ gradle test -:test - -HelloWorldTest > helloAnotherSampleName SKIPPED - -HelloWorldTest > helloNoName FAILED - org.junit.ComparisonFailure: expected: but was: - at org.junit.Assert.assertEquals(Assert.java:125) - at org.junit.Assert.assertEquals(Assert.java:147) - at HelloWorldTest.helloNoName(HelloWorldTest.java:10) - -HelloWorldTest > helloSampleName PASSED - -3 tests completed, 1 failed, 1 skipped -``` - -Wait... didn't we just fix the test? Why is it failing? Take a closer look... - -In fact, `helloSampleName()` *is* passing. It's just that at the same time, -we just inadvertently broke that first test: `helloNoName()`. - -This is one tiny example of the benefit of maintaining a test suite: if we -use them to drive out our code, the second we break the program the tests -say so. Since we saw them passing just *before* our latest change, -whatever we *just* did most likely cause that regression. - -Our latest change was making the greeting dependent on the name given. Our -first test expects that if either a blank string or null are given as the -name, then "`World`" should be substituted in. Let's implement that. - -`src/main/java/HelloWorld.java`: -```java -public class HelloWorld { - public static String hello(String name) { - if(name == null || "".equals(name)) { - name = "World"; - } - return "Hello, " + name + "!"; - } -} -``` - -... and re-run the tests ... - -``` -$ gradle test -... -:test - -HelloWorldTest > helloNoName PASSED - -HelloWorldTest > helloSampleName PASSED - -HelloWorldTest > helloAnotherSampleName SKIPPED - -BUILD SUCCESSFUL - -Total time: 4.804 secs -``` - -Excellent! We're now (at least) two-thirds the way done. Just one more -test to go... - - - -## Enabling the last test - -(Re)open `src/test/java/HelloWorldTest.java` and find the last test: - -```java -... -@Test -@Ignore -public void helloAnotherSampleName() { - assertEquals("Hello, Bob!", HelloWorld.hello("Bob")); -} -... -``` - -... and remove it's `@Ignore` to enable it ... - -```java -... -@Test -public void helloAnotherSampleName() { - assertEquals("Hello, Bob!", HelloWorld.hello("Bob")); -} -... -``` - -... and re-run the tests ... - -``` -$ gradle test -:compileJava UP-TO-DATE -:processResources UP-TO-DATE -:classes UP-TO-DATE -:compileTestJava -:processTestResources UP-TO-DATE -:testClasses -:test - -HelloWorldTest > helloNoName PASSED - -HelloWorldTest > helloSampleName PASSED - -HelloWorldTest > helloAnotherSampleName PASSED - -BUILD SUCCESSFUL - -Total time: 6.953 secs -``` - -Oh, hello! Turns out, the solution we put into place didn't just apply for -"`Alice`" but "`Bob`" equally well. In this case, the test succeeded with -no additional code on our part. - - - -## Refactoring - -Now that you've got all the tests passing, you might consider whether -the code is in the most readable/maintainable/efficient shape. What makes -for "good" design of software is a big topic. The pursuit of it underlies -much of what makes up the more valuable conversations on Exercism. - -For now, let's just take a quick review of our solution and see if there's -any part of it we'd like to refactor. Refactoring is changing the the way -a bit of code reads without changing what it does. - -Right now, the details of detecting whether the caller of `hello()` has -given a name or not (i.e. `name` is either `null` or an empty string) is -sitting right next to the core responsibility of the method: to produce a -personalized greeting. - -```java -public class HelloWorld { - public static String hello(String name) { - if(name == null || "".equals(name)) { - name = "World"; - } - return "Hello, " + name + "!"; - } -} -``` - -How would things read if we extracted those details into a separate method -and at the same time, replaced the `if` with a ternary expression? - -```java -public class HelloWorld { - - public static String hello(String name) { - String whom = isBlank(name) ? "World" : name; - return "Hello, " + whom + "!"; - } - - private static boolean isBlank(String string) { - return string == null || "".equals(string); - } -} -``` - -By extracting that logic into the `isBlank()` method, we've added a little -abstraction to our program -- it's not as literal as it was before. Yet, -it allows us to defer *needing* to understand *how* "blankness" is -detected. If we can assume that `isBlank()` just works, we don't have to -downshift in our head to those details. Instead, we can remain at the same -level of thinking: to whom are we greeting? +The following instructions work equally well on Windows, Mac OS X and Linux. -The ternary operator allowed us to express that choice in a more compact -form. Less to read without losing the intent. +## Solve "Hello World" -Finally, we introduced another variable: `whom`. Doing so gives a name to -the output of the ternary expression. We certainly could have continued -to reuse `name`, but by introducing a second `String` to hold the -calculated value this keeps crisp the two ideas: `name` is what's given -and `whom` is what's been determined. +Try writing a solution that passes one test at a time, running Gradle each time: -We made a bunch of changes, let's make sure we didn't break the program! ``` $ gradle test -... -HelloWorldTest > helloNoName PASSED - -HelloWorldTest > helloSampleName PASSED - -HelloWorldTest > helloAnotherSampleName PASSED -... ``` -This illustrates another benefit of writing tests: you can make significant -changes to the structure of the program and very quickly restore your -confidence that the program still works. These tests are a far cry from a -"proof" of correctness, but well-written tests do a much better job of -(very quickly) giving us evidence that it is. Without them, we manually -run the program with different inputs and/or inspecting the code -line-by-line -- time-consuming and error prone. - +## Iterate through the tests +After your first test passes, remove the `@Ignore` from the next test, and ierate on your solution, +testing after each change. -# Submitting your first iteration +## All the tests pass? Submit your solution! With a working solution that we've reviewed, we're ready to submit it to exercism.io. @@ -503,72 +32,17 @@ exercism.io. $ exercism submit src/main/java/HelloWorld.java ``` - - -# Next Steps +## Next Steps From here, there are a number of paths you can take. +1. Move on to the next exercise +2. Review (and comment on) others' submissions to this exercise +3. Submit another iteration +4. Contribute to Exercism -## Move on to the next exercise - -There are many more exercises you can practice with. Grab the next one! - -``` -$ exercism fetch java -``` - - -## Review (and comment on) others' submissions to this exercise - -The heart of Exercism are the conversations about coding -practices. It's definitely fun to practice, but engaging with others -both in their attempts and your own is how you get feedback. That feedback -can help point out what you're doing well and where you might need to -improve. - -Some submissions will be nearly identical to yours; others will be -completely different. Seeing both kinds can be instructive and interesting. - -Note that you can only view submissions of others for exercises you have -completed yourself. This enriches the experience of reading others' code -because you'll have your own experience of trying to solve the problem. - -Here's an up-to-date list of submissions on the Java track: - -http://exercism.io/tracks/java/exercises - - - -## Submit another iteration - -You are also encouraged to consider additional "requirements" on a given -exercise. - -For example, you could add a test or two that requires that the greeting -use the capitalized form on the person's name, regardless of the case they -used. - -In that situation, you'd write a test to set-up that new expectation and -then implement that in the code (the same process we just went through -together, above). - - - -## Contribute to Exercism - -The entire of Exercism is Open Source and is the labor of love for over -100 maintainers and many more contributors. - -A starting point to jumping in can be found here: - -https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md - - ----- -Regardless of what you decide to do next, we sincerely hope you learn -and enjoy being part of this community. If at any time you need assistance +We sincerely hope you learn and enjoy being part of this community. If at any time you need assistance do not hesitate to ask for help: http://exercism.io/languages/java/help diff --git a/exercises/hello-world/TUTORIAL.md b/exercises/hello-world/TUTORIAL.md new file mode 100644 index 000000000..f2f963e8d --- /dev/null +++ b/exercises/hello-world/TUTORIAL.md @@ -0,0 +1,576 @@ +NOTE: You can also view the HTML version of this file here: +https://github.com/exercism/xjava/blob/master/exercises/hello-world/TUTORIAL.md + +* [Solving "Hello, World!"](#solving-hello-world) + * [Reading Gradle output](#reading-gradle-output) + * [Fixing the first failing test](#fixing-the-first-failing-test) + * [Enabling and fixing the second test](#enabling-and-fixing-the-second-test) + * [Enabling the last test](#enabling-the-last-test) + * [Refactoring](#refactoring) +* [Submitting your first iteration](#submitting-your-first-iteration) +* [Next Steps](#next-steps) + * [Review (and comment on) others' submissions to this exercise](#review-and-comment-on-others-submissions-to-this-exercise) + * [Extend an exercise](#extend-an-exercise) + +---- + +# Solving "Hello, World!" + +Welcome to the first exercise on the Java track! + +This is a step-by-step guide to solving this exercise. + +Each exercise comes with a set of tests. The first pass through the +exercise is about getting all of the tests to pass, one at a time. + +If you have not installed the Java Development Kit and Gradle, you must do +so now. For help with this, see: http://exercism.io/languages/java/installing + +---- + +This guide picks-up where [Running the Tests (in Java)](http://exercism.io/languages/java/tests) +left off. If you haven't reviewed those instructions, do so now. + +The following instructions work equally well on Windows, Mac OS X and Linux. + +## Reading Gradle output + +Use Gradle to run the tests: + +``` +$ gradle test +``` + +This command does a lot and displays a bunch of stuff. Let's break it down... + +``` +:compileJava +:processResources UP-TO-DATE +:classes +``` + +Each line that begins with a colon (like `:compileJava`) is Gradle telling +us that it's starting that task. The first three tasks are about compiling +the source code of our *solution*. We've done you a favor and included just +enough code for the solution that it compiles. + +When a task is successful, it generally does not output anything. This is +why `:compileJava` and `:classes` do not produce any additional output. +`:processResources` reports that it had nothing to do. + +So far, so good... + +The next three tasks are about compiling source code of the *tests*. + +``` +:compileTestJava +:processTestResources UP-TO-DATE +:testClasses +``` + +... with both sets of source code successfully compiled, Gradle turns to +running the task you asked it to: executing the tests against the solution. + +``` +:test + +HelloWorldTest > helloNoName FAILED + java.lang.AssertionError: expected: but was: + at org.junit.Assert.fail(Assert.java:93) + at org.junit.Assert.failNotEquals(Assert.java:647) + at org.junit.Assert.assertEquals(Assert.java:128) + at org.junit.Assert.assertEquals(Assert.java:147) + at HelloWorldTest.helloNoName(HelloWorldTest.java:10) + +HelloWorldTest > helloSampleName SKIPPED + +HelloWorldTest > helloAnotherSampleName SKIPPED + +3 tests completed, 1 failed, 2 skipped +:test FAILED + +FAILURE: Build failed with an exception. + +* What went wrong: +Execution failed for task ':test'. +> There were failing tests. See the report at: file:///Users/jtigger/projects/exercism/xjava/build/exercism/java/hello-world/build/reports/tests/index.html + +* Try: +Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. + +BUILD FAILED + +Total time: 6.716 secs +``` + +Seeing the word "fail" TEN TIMES might give you the impression you've done +something horribly wrong. You haven't. It's a whole lot of noise over +a single test not passing. + +Let's focus in on the important bits: + +``` +HelloWorldTest > helloNoName FAILED + java.lang.AssertionError: expected: but was: +``` + +...is read: "Within the test class named `HelloWorldTest`, the test method +`helloNoName` did not pass because the solution did not satisfy an +assertion. Apparently, we expected to see the string 'Hello, World!' but +the value `null` was returned instead. + +The last line of the stack trace tells us exactly where this unsatisfied +assertion lives: + +``` + at HelloWorldTest.helloNoName(HelloWorldTest.java:10) +``` + +Looks like the crime was discovered on line 10 in the test file. + +Knowing these two facts, + +1. the return value was not what was expected, and +2. the failure was on line 10 of the test, + +we can turn this failure into success. + + + +## Fixing the first failing test + +In your favorite text editor, open `src/test/java/HelloWorldTest.java` +and go to line 10. + +```java +assertEquals("Hello, World!", HelloWorld.hello("")); +``` + +The test is expecting that `hello()`, when given an empty string (`""`), +returns "Hello, World!". Instead, `hello()` is returning `null`. +Let's fix that. + +Open `src/main/java/HelloWorld.java`. + +```java +public class HelloWorld { + public static String hello(String name) { + return null; + } +} +``` + +Let's change that to return the expected string: + +```java +public class HelloWorld { + public static String hello(String name) { + return "Hello, World!"; + } +} +``` + +Save the file and run the tests again: + +``` +$ gradle test +:compileJava +:processResources UP-TO-DATE +:classes +:compileTestJava +:processTestResources UP-TO-DATE +:testClasses +:test + +HelloWorldTest > helloAnotherSampleName SKIPPED + +HelloWorldTest > helloNoName PASSED + +HelloWorldTest > helloSampleName SKIPPED + +BUILD SUCCESSFUL + +Total time: 4.523 secs +``` + +"BUILD SUCCESSFUL"! Woohoo! :) You can see that `helloNoName()` test is +now passing. + +With one win under our belt, we can turn our focus to some other messages +that we've been ignoring: the lines ending in "`SKIPPED`". + +Each test suite contains a series of tests, all of which have been marked +to be skipped/ignored except the first one. We did this to help you focus +on getting one test running at a time. + +Let's tackle the next test... + + + +## Enabling and fixing the second test + +Right now, that second test is being skipped/ignored. Let's enable it. + +(Re)open `src/test/java/HelloWorldTest.java` and find the second test: + +```java +... +@Test +@Ignore +public void helloSampleName() { + assertEquals("Hello, Alice!", HelloWorld.hello("Alice")); +} +... +``` + +When the JUnit test runner sees that `@Ignore` annotation on the test +method, it knows to skip over that test. Remove that line: + +```java +... +@Test +public void helloSampleName() { + assertEquals("Hello, Alice!", HelloWorld.hello("Alice")); +} +... +``` + +Now, when you run the tests, both tests run: + +```sh +$ gradle test +... +:test + +HelloWorldTest > helloNoName PASSED + +HelloWorldTest > helloSampleName FAILED + org.junit.ComparisonFailure: expected: but was: + at org.junit.Assert.assertEquals(Assert.java:125) + at org.junit.Assert.assertEquals(Assert.java:147) + at HelloWorldTest.helloSampleName(HelloWorldTest.java:16) + +HelloWorldTest > helloAnotherSampleName SKIPPED + +3 tests completed, 1 failed, 1 skipped +... +``` + +The first test, `helloNoName()` continues to pass. We see that +`helloSampleName` -- the test we just un-`@Ignore`'d -- is now running and +failing. Yay, failing test! In fact, the "failure" message is just +describing the difference between what the program does now and what it +should do for us to call it "done." + +Right now, we've hardcoded the greeting. Enabling this second test has +unleashed a new expectation: that our program incorporate a name given +into that greeting. When given the name "`Alice`", that's who should be +greeted instead of "`World`". + +(Re)open `src/main/java/HelloWorld.java`. + +```java +public class HelloWorld { + public static String hello(String name) { + return "Hello, World!"; + } +} +``` + +While `hello()` does accept a reference to a string named `name`, it is not +using it in the output. Let's change that: + + +```java +public class HelloWorld { + public static String hello(String name) { + return "Hello, " + name + "!"; + } +} +``` + +... and rerun the tests ... + +``` +$ gradle test +:test + +HelloWorldTest > helloAnotherSampleName SKIPPED + +HelloWorldTest > helloNoName FAILED + org.junit.ComparisonFailure: expected: but was: + at org.junit.Assert.assertEquals(Assert.java:125) + at org.junit.Assert.assertEquals(Assert.java:147) + at HelloWorldTest.helloNoName(HelloWorldTest.java:10) + +HelloWorldTest > helloSampleName PASSED + +3 tests completed, 1 failed, 1 skipped +``` + +Wait... didn't we just fix the test? Why is it failing? Take a closer look... + +In fact, `helloSampleName()` *is* passing. It's just that at the same time, +we just inadvertently broke that first test: `helloNoName()`. + +This is one tiny example of the benefit of maintaining a test suite: if we +use them to drive out our code, the second we break the program the tests +say so. Since we saw them passing just *before* our latest change, +whatever we *just* did most likely cause that regression. + +Our latest change was making the greeting dependent on the name given. Our +first test expects that if either a blank string or null are given as the +name, then "`World`" should be substituted in. Let's implement that. + +`src/main/java/HelloWorld.java`: +```java +public class HelloWorld { + public static String hello(String name) { + if(name == null || "".equals(name)) { + name = "World"; + } + return "Hello, " + name + "!"; + } +} +``` + +... and re-run the tests ... + +``` +$ gradle test +... +:test + +HelloWorldTest > helloNoName PASSED + +HelloWorldTest > helloSampleName PASSED + +HelloWorldTest > helloAnotherSampleName SKIPPED + +BUILD SUCCESSFUL + +Total time: 4.804 secs +``` + +Excellent! We're now (at least) two-thirds the way done. Just one more +test to go... + + + +## Enabling the last test + +(Re)open `src/test/java/HelloWorldTest.java` and find the last test: + +```java +... +@Test +@Ignore +public void helloAnotherSampleName() { + assertEquals("Hello, Bob!", HelloWorld.hello("Bob")); +} +... +``` + +... and remove it's `@Ignore` to enable it ... + +```java +... +@Test +public void helloAnotherSampleName() { + assertEquals("Hello, Bob!", HelloWorld.hello("Bob")); +} +... +``` + +... and re-run the tests ... + +``` +$ gradle test +:compileJava UP-TO-DATE +:processResources UP-TO-DATE +:classes UP-TO-DATE +:compileTestJava +:processTestResources UP-TO-DATE +:testClasses +:test + +HelloWorldTest > helloNoName PASSED + +HelloWorldTest > helloSampleName PASSED + +HelloWorldTest > helloAnotherSampleName PASSED + +BUILD SUCCESSFUL + +Total time: 6.953 secs +``` + +Oh, hello! Turns out, the solution we put into place didn't just apply for +"`Alice`" but "`Bob`" equally well. In this case, the test succeeded with +no additional code on our part. + + + +## Refactoring + +Now that you've got all the tests passing, you might consider whether +the code is in the most readable/maintainable/efficient shape. What makes +for "good" design of software is a big topic. The pursuit of it underlies +much of what makes up the more valuable conversations on Exercism. + +For now, let's just take a quick review of our solution and see if there's +any part of it we'd like to refactor. Refactoring is changing the the way +a bit of code reads without changing what it does. + +Right now, the details of detecting whether the caller of `hello()` has +given a name or not (i.e. `name` is either `null` or an empty string) is +sitting right next to the core responsibility of the method: to produce a +personalized greeting. + +```java +public class HelloWorld { + public static String hello(String name) { + if(name == null || "".equals(name)) { + name = "World"; + } + return "Hello, " + name + "!"; + } +} +``` + +How would things read if we extracted those details into a separate method +and at the same time, replaced the `if` with a ternary expression? + +```java +public class HelloWorld { + + public static String hello(String name) { + String whom = isBlank(name) ? "World" : name; + return "Hello, " + whom + "!"; + } + + private static boolean isBlank(String string) { + return string == null || "".equals(string); + } +} +``` + +By extracting that logic into the `isBlank()` method, we've added a little +abstraction to our program -- it's not as literal as it was before. Yet, +it allows us to defer *needing* to understand *how* "blankness" is +detected. If we can assume that `isBlank()` just works, we don't have to +downshift in our head to those details. Instead, we can remain at the same +level of thinking: to whom are we greeting? + +The ternary operator allowed us to express that choice in a more compact +form. Less to read without losing the intent. + +Finally, we introduced another variable: `whom`. Doing so gives a name to +the output of the ternary expression. We certainly could have continued +to reuse `name`, but by introducing a second `String` to hold the +calculated value this keeps crisp the two ideas: `name` is what's given +and `whom` is what's been determined. + +We made a bunch of changes, let's make sure we didn't break the program! + +``` +$ gradle test +... +HelloWorldTest > helloNoName PASSED + +HelloWorldTest > helloSampleName PASSED + +HelloWorldTest > helloAnotherSampleName PASSED +... +``` + +This illustrates another benefit of writing tests: you can make significant +changes to the structure of the program and very quickly restore your +confidence that the program still works. These tests are a far cry from a +"proof" of correctness, but well-written tests do a much better job of +(very quickly) giving us evidence that it is. Without them, we manually +run the program with different inputs and/or inspecting the code +line-by-line -- time-consuming and error prone. + + + +# Submitting your first iteration + +With a working solution that we've reviewed, we're ready to submit it to +exercism.io. + +``` +$ exercism submit src/main/java/HelloWorld.java +``` + + + +# Next Steps + +From here, there are a number of paths you can take. + + +## Move on to the next exercise + +There are many more exercises you can practice with. Grab the next one! + +``` +$ exercism fetch java +``` + + +## Review (and comment on) others' submissions to this exercise + +The heart of Exercism are the conversations about coding +practices. It's definitely fun to practice, but engaging with others +both in their attempts and your own is how you get feedback. That feedback +can help point out what you're doing well and where you might need to +improve. + +Some submissions will be nearly identical to yours; others will be +completely different. Seeing both kinds can be instructive and interesting. + +Note that you can only view submissions of others for exercises you have +completed yourself. This enriches the experience of reading others' code +because you'll have your own experience of trying to solve the problem. + +Here's an up-to-date list of submissions on the Java track: + +http://exercism.io/tracks/java/exercises + + + +## Submit another iteration + +You are also encouraged to consider additional "requirements" on a given +exercise. + +For example, you could add a test or two that requires that the greeting +use the capitalized form on the person's name, regardless of the case they +used. + +In that situation, you'd write a test to set-up that new expectation and +then implement that in the code (the same process we just went through +together, above). + + + +## Contribute to Exercism + +The entire of Exercism is Open Source and is the labor of love for over +100 maintainers and many more contributors. + +A starting point to jumping in can be found here: + +https://github.com/exercism/x-common/blob/master/CONTRIBUTING.md + + +---- + +Regardless of what you decide to do next, we sincerely hope you learn +and enjoy being part of this community. If at any time you need assistance +do not hesitate to ask for help: + +http://exercism.io/languages/java/help + +Cheers!