dtonhofer

dtonhofer

Functional Programming in Java, Second Edition: Functional Programming in Java, Second Edition: JUnit code improvements for Chapter 11, pages 192 ff “Refactoring File Processing”

In this code:

  • We actually create the (temporary) file we want to read and delete it again at the end.
  • Default charsets for reading text files are the work of the devil, we set UTF-8 explicitly.
  • The files need to be properly closed with try-with-resources, this also applies to the stream approach.
  • A “long” count as return value seems excessive, dropping to int.
package chapter11;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class FileProcessingTest {

    private static File actualFile;
    private final static Charset charset = StandardCharsets.UTF_8;

    // Creates a file "/tmp/WordCount13982583023783245787.java" for example

    @BeforeAll
    static void createTmpFile() throws IOException {
        actualFile = File.createTempFile("WordCount", ".java");
        try (FileWriter writer = new FileWriter(actualFile, charset)) {
            writer.write("package foo;\n");
            writer.write("public class X {\n");
            writer.write("}\n");
            writer.write("public class Y {\n");
            writer.write("}\n");
        }
    }

    @AfterAll
    static void deleteTmpFile() throws IOException {
        // or one could have installed a handler with deleteOnExit()
        actualFile.delete();
    }

    interface WordCount {

        int countInFile(String word, File file) throws IOException;

    }

    // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/FileReader.html
    // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/io/BufferedReader.html

    static class WordCountBefore implements WordCount {

        public int countInFile(final String searchWord, final File file) throws IOException {
            int count = 0;
            // Use try-with-resources / automatic resource management to clean up.
            // Specify the charset, "default chartset" is the devil's work!
            try (final var bufferedReader = new BufferedReader(new FileReader(file, charset))) {
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    final String[] words = line.split(" ");
                    for (String word : words) {
                        if (word.equals(searchWord)) {
                            count++;
                        }
                    }
                }
                return count;
            }
        }
    }

    // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/nio/file/Files.html

    static class WordCountAfter implements WordCount {

        // The Files.lines() call may throw IOException.
        // Additionally, the count() this yields a "long", so we need to cast to fit the interface.
        // We still must use try-with-resources to close the file.

        public int countInFile(final String searchWord, final File file) throws IOException {
            try (final Stream<String> stream = Files.lines(file.toPath(), charset)) {
                return (int) stream
                        .flatMap(line -> Stream.of(line.split(" ")))
                        .filter(word -> word.equals(searchWord))
                        .count();
            }
        }

    }

    private static void commonFileProcessingTests(final WordCount wordCount, File file) {
        assertAll(
                () -> assertEquals(2, wordCount.countInFile("public", file)),
                () -> assertEquals(1, wordCount.countInFile("package", file)));
    }

    @Test
    void fileProcessingBefore() {
        commonFileProcessingTests(new WordCountBefore(), actualFile);
    }

    @Test
    void fileProcessingAfter() {
        commonFileProcessingTests(new WordCountAfter(), actualFile);
    }

}

Where Next?

Popular Pragmatic Bookshelf topics Top

johnp
Running the examples in chapter 5 c under pytest 5.4.1 causes an AttributeError: ‘module’ object has no attribute ‘config’. In particula...
New
mikecargal
Title: Hands-on Rust: question about get_component (page 295) (feel free to respond. “You dug you’re own hole… good luck”) I have somet...
New
Chrichton
Dear Sophie. I tried to do the “Authorization” exercise and have two questions: When trying to plug in an email-service, I found the ...
New
curtosis
Running mix deps.get in the sensor_hub directory fails with the following error: ** (Mix) No SSH public keys found in ~/.ssh. An ssh aut...
New
jgchristopher
“The ProductLive.Index template calls a helper function, live_component/3, that in turn calls on the modal component. ” Excerpt From: Br...
New
brunogirin
When I run the coverage example to report on missing lines, I get: pytest --cov=cards --report=term-missing ch7 ERROR: usage: pytest [op...
New
jonmac
The allprojects block listed on page 245 produces the following error when syncing gradle: “org.gradle.api.GradleScriptException: A prob...
New
s2k
Hi all, currently I wonder how the Tailwind colours work (or don’t work). For example, in app/views/layouts/application.html.erb I have...
New
andreheijstek
After running /bin/setup, the first error was: The foreman' command exists in these Ruby versions: That was easy to fix: gem install fore...
New
davetron5000
Hello faithful readers! If you have tried to follow along in the book, you are asked to start up the dev environment via dx/build and ar...
New

Other popular topics Top

Devtalk
Hello Devtalk World! Please let us know a little about who you are and where you’re from :nerd_face:
New
DevotionGeo
I know that -t flag is used along with -i flag for getting an interactive shell. But I cannot digest what the man page for docker run com...
New
New
AstonJ
We have a thread about the keyboards we have, but what about nice keyboards we come across that we want? If you have seen any that look n...
New
AstonJ
There’s a whole world of custom keycaps out there that I didn’t know existed! Check out all of our Keycaps threads here: https://forum....
New
mafinar
Crystal recently reached version 1. I had been following it for awhile but never got to really learn it. Most languages I picked up out o...
New
Margaret
Hello everyone! This thread is to tell you about what authors from The Pragmatic Bookshelf are writing on Medium.
1145 27688 760
New
DevotionGeo
I have always used antique keyboards like Cherry MX 1800 or Cherry MX 8100 and almost always have modified the switches in some way, like...
New
AstonJ
If you’re getting errors like this: psql: error: connection to server on socket “/tmp/.s.PGSQL.5432” failed: No such file or directory ...
New
Fl4m3Ph03n1x
Background Lately I am in a quest to find a good quality TTS ai generation tool to run locally in order to create audio for some videos I...
New

Latest in Functional Programming in Java, Second Edition

Functional Programming in Java, Second Edition Portal

Sub Categories: