{"id":"unit-testing-scanner-input","slug":"test-input","title":"Test input for a Scanner","text":"Steps 1 and 5 of the checklist mean running a program again and again with the same input. Typing it every time is slow, and it's easy to type something a little different by mistake. Module 20 showed that a Scanner can read a String, and in module 25 the user interface was given its Scanner instead of making one. Put the two together, and any code that reads input can be tried with input written right in the code.\n\nThe trick is that the method that reads doesn't make its own Scanner: it gets one as a parameter. A hiker writes down the kilometers walked each day, one number per line, and an empty line ends the list:\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(totalKilometers(new Scanner(\"12\\n8\\n15\\n\")));\n        System.out.println(totalKilometers(new Scanner(\"7\\n\\n30\\n\")));\n        System.out.println(totalKilometers(new Scanner(\"\")));\n    }\n\n    public static int totalKilometers(Scanner scanner) {\n        int total = 0;\n        while (scanner.hasNextLine()) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            total += Integer.valueOf(line);\n        }\n        return total;\n    }\n}\n```\n\n```output\n35\n7\n0\n```\n\n- Inside the quotes, `\\n` is a line break, so `\"12\\n8\\n15\\n\"` is three lines, just as if someone had typed 12, 8 and 15, pressing Enter after each.\n- `\"7\\n\\n30\\n\"` has an empty line after the 7, so the method stops there and never reads the 30.\n- `\"\"` has no lines at all, like input that ends right away. It's easy to write here, and awkward to try by typing.\n- In the finished program, `main` gives the method the keyboard: `totalKilometers(new Scanner(System.in))`. The method works the same either way, because it only uses the Scanner it was given.\n\nA method that makes its own `new Scanner(System.in)` inside can only ever read the keyboard. Taking the Scanner as a parameter is what makes it easy to try out.\n\n### A check that says ok or wrong\n\nPrinting the results still means comparing them in your head, every time. Let the program do that too: give it the input and the result it should give, and have it print whether it did.\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        check(\"12\\n8\\n15\\n\", 35);\n        check(\"7\\n\\n30\\n\", 7);\n        check(\"\", 0);\n        check(\"5\\n\", 5);\n    }\n\n    public static void check(String input, int expected) {\n        int result = totalKilometers(new Scanner(input));\n        if (result == expected) {\n            System.out.println(\"ok\");\n        } else {\n            System.out.println(\"WRONG: expected \" + expected + \" but got \" + result);\n        }\n    }\n\n    public static int totalKilometers(Scanner scanner) {\n        int total = 0;\n        while (scanner.hasNextLine()) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            total += Integer.valueOf(line);\n        }\n        return total;\n    }\n}\n```\n\n```output\nok\nok\nok\nok\n```\n\nNow any change to `totalKilometers` can be checked in a second. Say someone changes `break` to `continue`, to skip empty lines instead of stopping at them. Then the second check prints `WRONG: expected 7 but got 37`, and you know at once, without typing a single number.\n\nEach call to `check` is a small **test**: an input, the result it should give, and a comparison. Tests like these, which check one method or one class on its own, are called **unit tests**. Writing a `check` method for every program gets tedious, though. The next step uses JUnit, a library that runs your tests, does the comparing, and keeps track of which tests passed and which failed.\n","task":"A diary has one entry per line. Write the method `countEntries(Scanner scanner, String word)`: it reads lines until the input ends and returns how many of them contain the word. A line that has the word twice still counts once, and capital letters count as different letters, as they do for `contains`. Use the Scanner the method gets as its parameter: `main` gives it the keyboard, and the check gives it a String. The check runs:\n\n```java\nSystem.out.println(countEntries(new Scanner(\"ran 5 km\\nrain all day\\nran again\\n\"), \"ran\"));\n```\n\nand expects:\n\n```\n2\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        System.out.println(countEntries(scanner, \"ran\"));\n    }\n\n    public static int countEntries(Scanner scanner, String word) {\n        return 0;\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        System.out.println(countEntries(scanner, \"ran\"));\n    }\n\n    public static int countEntries(Scanner scanner, String word) {\n        int count = 0;\n        while (scanner.hasNextLine()) {\n            String entry = scanner.nextLine();\n            if (entry.contains(word)) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n","hints":["Read with `while (scanner.hasNextLine())`, keep each line in a variable, and count the lines for which `contains(word)` is true.","`if (entry.contains(word)) { count++; }` inside the loop, and `return count;` after it. `rain` doesn't contain `ran`: the letters must come in a row."],"tests":[{"name":"System.out.println(countEntries(new Scanner(\"ran 5 km\\nrain all day\\nran again\\n\"), \"ran\"));","stdin":"","call":"System.out.println(countEntries(new Scanner(\"ran 5 km\\nrain all day\\nran again\\n\"), \"ran\"));\n","expect":"2","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(countEntries(new Scanner(\"\"), \"tea\"));\nSystem.out.println(countEntries(new Scanner(\"tea, then more tea\\ncoffee\\n\"), \"tea\"));\nSystem.out.println(countEntries(new Scanner(\"Tea with Mia\\nmint tea\\n\"), \"tea\"));\n","expect":"0\n1\n1","hidden":true},{"name":"Test 3","stdin":"ran to the lake\nslept in\nran home\nread a book\n","expect":"2","hidden":true}],"require":[{"pattern":"new\\s+Scanner\\s*\\(\\s*System\\.in\\s*\\)","max":1,"message":"Use the Scanner that `countEntries` gets as its parameter. Don't make another one from `System.in`."}],"forbid":[],"more":[{"task":"`countLong` should count the words that have at least 5 letters, and `main` checks it with four inputs. But the method has a bug. What does the program print?\n","kind":"predict","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        check(\"apple\\nfig\\nbanana\\n\", 2);\n        check(\"kiwi\\n\", 0);\n        check(\"\", 0);\n        check(\"melon\\nlemon\\n\", 2);\n    }\n\n    public static void check(String input, int expected) {\n        int result = countLong(new Scanner(input));\n        if (result == expected) {\n            System.out.println(\"ok\");\n        } else {\n            System.out.println(\"WRONG: expected \" + expected + \" but got \" + result);\n        }\n    }\n\n    public static int countLong(Scanner scanner) {\n        int count = 0;\n        while (scanner.hasNextLine()) {\n            if (scanner.nextLine().length() > 5) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        check(\"apple\\nfig\\nbanana\\n\", 2);\n        check(\"kiwi\\n\", 0);\n        check(\"\", 0);\n        check(\"melon\\nlemon\\n\", 2);\n    }\n\n    public static void check(String input, int expected) {\n        int result = countLong(new Scanner(input));\n        if (result == expected) {\n            System.out.println(\"ok\");\n        } else {\n            System.out.println(\"WRONG: expected \" + expected + \" but got \" + result);\n        }\n    }\n\n    public static int countLong(Scanner scanner) {\n        int count = 0;\n        while (scanner.hasNextLine()) {\n            if (scanner.nextLine().length() > 5) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n","hints":["`> 5` counts only words of 6 letters or more, so a word of exactly 5 letters is missed. Go through each check's words and count what the method really counts.","In the first check only `banana` is counted, so it's 1 instead of 2. `kiwi` and the empty input give 0 as they should. `melon` and `lemon` both have exactly 5 letters, so neither is counted."],"tests":[{"name":"Output","stdin":"","expect":"WRONG: expected 2 but got 1\nok\nok\nWRONG: expected 2 but got 0","hidden":false}],"require":[],"forbid":[],"lines":["WRONG: expected 2 but got 1","ok","ok","WRONG: expected 2 but got 0"]},{"task":"A lap timer reads lap times in seconds, one per line, until the input ends. Its method `readLaps` makes its own Scanner, so it can only ever read the keyboard, and nobody has tried it with no laps at all. Do two things:\n\n- Change `readLaps` to take the Scanner as a parameter, `readLaps(Scanner scanner)`, and make `main` give it `new Scanner(System.in)`.\n- With no laps, `fastest()` crashes. Make it return 0 then.\n\nThe check runs:\n\n```java\nLapTimer timer = new LapTimer();\ntimer.readLaps(new Scanner(\"62\\n58\\n60\\n\"));\nSystem.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\nLapTimer none = new LapTimer();\nnone.readLaps(new Scanner(\"\"));\nSystem.out.println(none.count() + \" laps, fastest \" + none.fastest());\n```\n\nand expects:\n\n```\n3 laps, fastest 58\n0 laps, fastest 0\n```\n","kind":"code","seed":"// ==== LapTimer.java ====\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class LapTimer {\n    private ArrayList<Integer> laps;\n\n    public LapTimer() {\n        this.laps = new ArrayList<>();\n    }\n\n    public void readLaps() {\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            this.laps.add(Integer.valueOf(scanner.nextLine()));\n        }\n    }\n\n    public int count() {\n        return this.laps.size();\n    }\n\n    public int fastest() {\n        int fastest = this.laps.get(0);\n        for (int lap : this.laps) {\n            if (lap < fastest) {\n                fastest = lap;\n            }\n        }\n        return fastest;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        LapTimer timer = new LapTimer();\n        timer.readLaps();\n        System.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\n    }\n}\n","solution":"// ==== LapTimer.java ====\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class LapTimer {\n    private ArrayList<Integer> laps;\n\n    public LapTimer() {\n        this.laps = new ArrayList<>();\n    }\n\n    public void readLaps(Scanner scanner) {\n        while (scanner.hasNextLine()) {\n            this.laps.add(Integer.valueOf(scanner.nextLine()));\n        }\n    }\n\n    public int count() {\n        return this.laps.size();\n    }\n\n    public int fastest() {\n        if (this.laps.isEmpty()) {\n            return 0;\n        }\n        int fastest = this.laps.get(0);\n        for (int lap : this.laps) {\n            if (lap < fastest) {\n                fastest = lap;\n            }\n        }\n        return fastest;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        LapTimer timer = new LapTimer();\n        timer.readLaps(new Scanner(System.in));\n        System.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\n    }\n}\n","hints":["Move the Scanner from the first line of `readLaps` into its parentheses: `public void readLaps(Scanner scanner)`. In `main`, call `timer.readLaps(new Scanner(System.in));`, and add `import java.util.Scanner;` to `Main.java`.","`this.laps.get(0)` crashes on an empty list. Start `fastest()` with `if (this.laps.isEmpty()) { return 0; }`."],"tests":[{"name":"Test 1","stdin":"","call":"LapTimer timer = new LapTimer();\ntimer.readLaps(new Scanner(\"62\\n58\\n60\\n\"));\nSystem.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\nLapTimer none = new LapTimer();\nnone.readLaps(new Scanner(\"\"));\nSystem.out.println(none.count() + \" laps, fastest \" + none.fastest());\n","expect":"3 laps, fastest 58\n0 laps, fastest 0","hidden":false},{"name":"Test 2","stdin":"","call":"LapTimer timer = new LapTimer();\ntimer.readLaps(new Scanner(\"75\\n\"));\nSystem.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\ntimer.readLaps(new Scanner(\"80\\n71\\n\"));\nSystem.out.println(timer.count() + \" laps, fastest \" + timer.fastest());\n","expect":"1 laps, fastest 75\n3 laps, fastest 71","hidden":true},{"name":"Test 3","stdin":"49\n47\n51\n","expect":"3 laps, fastest 47","hidden":true}],"require":[{"pattern":"\\bvoid\\s+readLaps\\s*\\(\\s*Scanner\\s+\\w+\\s*\\)","message":"Give `readLaps` the Scanner as its parameter: `public void readLaps(Scanner scanner)`."},{"pattern":"new\\s+Scanner\\s*\\(\\s*System\\.in\\s*\\)","max":1,"message":"Make only one Scanner from `System.in`, in `main`, and give it to `readLaps`."}],"forbid":[]}]}