dtonhofer

dtonhofer

Functional Programming in Java, Second Edition: JUnit code improvements for Chapter 11, pages 181 ff "Refactoring the Traditional for loop"

Suggestions for some JUnit test improvements in Refactoring the Traditional “for” loop p 181 ff

Below code for testing the original/classical “factorial” and the stream-based “factorial” .

The following changes have been made:

  • The Factorial class has been given a constructor printing something when it is called (just for fun)
  • Creating BigInteger instances is annoying, so we use the bigOf() static method to create them.
  • The computeFactorial() function throws an exception if negative values are passsed in, as it should.
  • In order to compare an existing class’ behaviour against the behaviour of newly implemented class (supposed to be a drop-in replacement), we need to identify the methods relevant to the test, factor them out into a new interface, and have the new and old classes implement that interface (in the real world, fixing the old code in that way may not always be possible though). Proceeding according to this recipe allows us to use the same test code to exercise the old and new classes with the same test harness - no test code duplication is needed for the two separate classes.
  • There is no valid reason to have a @BeforeEach method as shown in the book, as that method is only called once even in the presence of assertAll(). It is cleaner to move the initialization of Factorialinto the individual @Test methods to keep everything local.
  • Testing of some “failure cases” (here, one case) has been added. Those are often forgotten in real life.
package chapter11;

import org.junit.jupiter.api.Test;

import java.math.BigInteger;
import java.util.stream.LongStream;

import static org.junit.jupiter.api.Assertions.*;

public class TraditionalForLoopTest {

    interface Factorial {

        BigInteger compute(long upTo);

    }

    static class FactorialBefore implements Factorial {

        public FactorialBefore() {
            System.out.println("Created " + getClass().getName());
        }

        public BigInteger compute(long upTo) {
            if (upTo < 0) {
                throw new IllegalArgumentException("The passed values is < 0: " + upTo);
            }
            BigInteger result = BigInteger.ONE;
            for (int i = 1; i <= upTo; i++) {
                result = result.multiply(BigInteger.valueOf(i));
            }
            return result;
        }

    }

    static class FactorialAfter implements Factorial {

        public FactorialAfter() {
            System.out.println("Created " + getClass().getName());
        }

        public BigInteger compute(long upTo) {
            if (upTo < 0) {
                throw new IllegalArgumentException("The passed values is < 0: " + upTo);
            }
            return LongStream.rangeClosed(1, upTo)
                    .mapToObj(BigInteger::valueOf)
                    .reduce(BigInteger.ONE, BigInteger::multiply);
        }

    }

    private static BigInteger bigOf(long x) {
        return BigInteger.valueOf(x);
    }

    private static void commonFactorialTests(final Factorial factorial) {
        assertAll(
                () -> assertEquals(bigOf(1), factorial.compute(0)),
                () -> assertEquals(bigOf(1), factorial.compute(1)),
                () -> assertEquals(bigOf(2), factorial.compute(2)),
                () -> assertEquals(bigOf(6), factorial.compute(3)),
                () -> assertEquals(bigOf(120), factorial.compute(5)),
                () -> assertThrows(IllegalArgumentException.class, () -> factorial.compute(-1))
        );
    }

    @Test
    void factorialBefore() {
        commonFactorialTests(new FactorialBefore());
    }

    @Test
    void factorialAfter() {
        commonFactorialTests(new FactorialAfter());
    }

}

Where Next?

Popular Pragmatic Bookshelf topics Top

iPaul
page 37 ANTLRInputStream input = new ANTLRInputStream(is); as of ANTLR 4 .8 should be: CharStream stream = CharStreams.fromStream(i...
New
telemachus
Python Testing With Pytest - Chapter 2, warnings for “unregistered custom marks” While running the smoke tests in Chapter 2, I get these...
New
jdufour
Hello! On page xix of the preface, it says there is a community forum "… for help if your’re stuck on one of the exercises in this book… ...
New
adamwoolhether
I’m not quite sure what’s going on here, but I’m unable to have to containers successfully complete the Readiness/Liveness checks. I’m im...
New
brunogirin
When installing Cards as an editable package, I get the following error: ERROR: File “setup.py” not found. Directory cannot be installe...
New
brunogirin
When running tox for the first time, I got the following error: ERROR: InterpreterNotFound: python3.10 I realised that I was running ...
New
hazardco
On page 78 the following code appears: &lt;%= link_to ‘Destroy’, product, class: ‘hover:underline’, method: :delete, data: { confirm...
New
AufHe
I’m a newbie to Rails 7 and have hit an issue with the bin/Dev script mentioned on pages 112-113. Iteration A1 - Seeing the list of prod...
New
taguniversalmachine
It seems the second code snippet is missing the code to set the current_user: current_user: Accounts.get_user_by_session_token(session["...
New
creminology
Skimming ahead, much of the following is explained in Chapter 3, but new readers (like me!) will hit a roadblock in Chapter 2 with their ...
New

Other popular topics Top

DevotionGeo
I know that these benchmarks might not be the exact picture of real-world scenario, but still I expect a Rust web framework performing a ...
New
AstonJ
You might be thinking we should just ask who’s not using VSCode :joy: however there are some new additions in the space that might give V...
New
PragmaticBookshelf
From finance to artificial intelligence, genetic algorithms are a powerful tool with a wide array of applications. But you don't need an ...
New
AstonJ
I ended up cancelling my Moonlander order as I think it’s just going to be a bit too bulky for me. I think the Planck and the Preonic (o...
New
AstonJ
Do the test and post your score :nerd_face: :keyboard: If possible, please add info such as the keyboard you’re using, the layout (Qw...
New
DevotionGeo
The V Programming Language Simple language for building maintainable programs V is already mentioned couple of times in the forum, but I...
New
rustkas
Intensively researching Erlang books and additional resources on it, I have found that the topic of using Regular Expressions is either c...
New
PragmaticBookshelf
Author Spotlight Rebecca Skinner @RebeccaSkinner Welcome to our latest author spotlight, where we sit down with Rebecca Skinner, auth...
New
PragmaticBookshelf
Programming Ruby is the most complete book on Ruby, covering both the language itself and the standard library as well as commonly used t...
New
First poster: bot
zig/http.zig at 7cf2cbb33ef34c1d211135f56d30fe23b6cacd42 · ziglang/zig. General-purpose programming language and toolchain for maintaini...
New

Latest in Functional Programming in Java, Second Edition

Functional Programming in Java, Second Edition Portal

Sub Categories: