
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 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 Pragmatic Bookshelf topics

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

Title: Web Development with Clojure, Third Edition - migrations/create not working: p159
When I execute the command:
user=> (create-...
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

I ran this command after installing the sample application:
$ cards add do something --owner Brian
And got a file not found error:
Fil...
New

This is as much a suggestion as a question, as a note for others.
Locally the SGP30 wasn’t available, so I ordered a SGP40. On page 53, ...
New

I think I might have found a problem involving SwitchCompat, thumbTint, and trackTint.
As entered, the SwitchCompat changes color to hol...
New

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

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

@mfazio23
I’m following the indications of the book and arriver ad chapter 10, but the app cannot be compiled due to an error in the Bas...
New

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

No chair. I have a standing desk.
This post was split into a dedicated thread from our thread about chairs :slight_smile:
New

poll
poll
Be sure to check out @Dusty’s article posted here: An Introduction to Alternative Keyboard Layouts It’s one of the best write-...
New

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

I have seen the keycaps I want - they are due for a group-buy this week but won’t be delivered until October next year!!! :rofl:
The Ser...
New

Tailwind CSS is an exciting new CSS framework that allows you to design your site by composing simple utility classes to create complex e...
New

I am asking for any distro that only has the bare-bones to be able to get a shell in the server and then just install the packages as we ...
New

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

Hello,
I’m a beginner in Android development and I’m facing an issue with my project setup. In my build.gradle.kts file, I have the foll...
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

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
Categories:
Sub Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /rails
- /js
- /python
- /security
- /go
- /swift
- /vim
- /clojure
- /emacs
- /haskell
- /java
- /onivim
- /typescript
- /svelte
- /kotlin
- /crystal
- /c-plus-plus
- /tailwind
- /gleam
- /react
- /ocaml
- /flutter
- /elm
- /vscode
- /ash
- /html
- /opensuse
- /centos
- /php
- /deepseek
- /zig
- /scala
- /textmate
- /sublime-text
- /lisp
- /nixos
- /debian
- /react-native
- /agda
- /kubuntu
- /arch-linux
- /django
- /revery
- /ubuntu
- /spring
- /manjaro
- /nodejs
- /diversity
- /lua
- /julia
- /c
- /slackware
- /markdown