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 thebigOf()
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 ofassertAll()
. It is cleaner to move the initialization ofFactorial
into 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());
}
}
Popular Pragprog topics

page 20: … protoc command…
I had to additionally run the following go get commands in order to be able to compile protobuf code using go...
New

Title: Web Development with Clojure, Third Edition, pg 116
Hi - I just started chapter 5 and I am stuck on page 116 while trying to star...
New

The following is cross-posted from the original Ray Tracer Challenge forum, from a post by garfieldnate. I’m cross-posting it so that the...
New

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

Hi @venkats,
It has been mentioned in the description of ‘Supervisory Job’ title that 2 things as mentioned below result in the same eff...
New

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

On page 78 the following code appears:
<%= link_to ‘Destroy’, product,
class: ‘hover:underline’,
method: :delete,
data: { confirm...
New

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

Hi, I’ve got a question about the implementation of PubSub when using a Phoenix.Socket.Transport behaviour rather than channels.
Before ...
New

Book: Programming Phoenix LiveView, page 142 (157/378), file lib/pento_web/live/product_live/form_component.ex, in the function below:
d...
New
Other popular topics

I am thinking in building or buy a desktop computer for programing, both professionally and on my free time, and my choice of OS is Linux...
New

Hello content creators! Happy new year. What tech topics do you think will be the focus of 2021? My vote for one topic is ethics in tech...
New

Intensively researching Erlang books and additional resources on it, I have found that the topic of using Regular Expressions is either c...
New

We’ve talked about his book briefly here but it is quickly becoming obsolete - so he’s decided to create a series of 7 podcasts, the firs...
New

Rails 7 completely redefines what it means to produce fantastic user experiences and provides a way to achieve all the benefits of single...
New

I am trying to crate a game for the Nintendo switch, I wanted to use Java as I am comfortable with that programming language. Can you use...
New

Large Language Models like ChatGPT say The Darnedest Things.
The Errors They MakeWhy We Need to Document Them, and What We Have Decided ...
New

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

A Ruby-Centric Chat with Noel Rappin @noelrappin
Once you start noodling around with Ruby you quickly figure out, as Noel Rappi...
New

This is cool!
DEEPSEEK-V3 ON M4 MAC: BLAZING FAST INFERENCE ON APPLE SILICON
We just witnessed something incredible: the largest open-s...
New
Latest in Pragprog
Latest (all)
Categories:
My Saved Portals
-
None saved yet
Popular Portals
- /elixir
- /opensuse
- /rust
- /kotlin
- /ruby
- /erlang
- /python
- /clojure
- /react
- /quarkus
- /go
- /vapor
- /v
- /react-native
- /wasm
- /security
- /django
- /nodejs
- /centos
- /haskell
- /rails
- /fable
- /gleam
- /swift
- /js
- /deno
- /assemblyscript
- /tailwind
- /laravel
- /symfony
- /phoenix
- /crystal
- /typescript
- /debian
- /adonisjs
- /julia
- /arch-linux
- /svelte
- /spring
- /preact
- /flutter
- /c-plus-plus
- /actix
- /java
- /angular
- /ocaml
- /zig
- /kubuntu
- /scala
- /zotonic
- /vim
- /rocky
- /lisp
- /html
- /keyboards
- /nim
- /vuejs
- /emacs
- /elm
- /nerves