
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

Following the steps described in Chapter 6 of the book, I’m stuck with running the migration as described on page 84:
bundle exec sequel...
New

The answer to 3rd Problem of Chapter 5 (Making Choices) of “Practical Programming, Third Edition” seems incorrect in the given answer ke...
New

Python Testing With Pytest - Chapter 2, warnings for “unregistered custom marks”
While running the smoke tests in Chapter 2, I get these...
New

Title: Hands-On Rust (Chap 8 (Adding a Heads Up Display)
It looks like
.with_simple_console_no_bg(SCREEN_WIDTH*2, SCREEN_HEIGHT*2...
New

I can’t setup the Rails source code. This happens in a working directory containing multiple (postgres) Rails apps.
With:
ruby-3.0.0
s...
New

Title: Web Development with Clojure, Third Edition, vB17.0 (p9)
The create table guestbook syntax suggested doesn’t seem to be accepted ...
New

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

The markup used to display the uploaded image results in a Phoenix.LiveView.HTMLTokenizer.ParseError error.
lib/pento_web/live/product_l...
New

Modern front-end development for Rails, second edition - Struggling to get the first chapter to work
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

Hello @herbert ! Trying to get the very first “Hello, Bracket Terminal!" example to run (p. 53). I develop on an Amazon EC2 instance runn...
New
Other popular topics

Reading something? Working on something? Planning something? Changing jobs even!?
If you’re up for sharing, please let us know what you’...
New

@AstonJ prompted me to open this topic after I mentioned in the lockdown thread how I started to do a lot more for my fitness.
https://f...
New

I’m thinking of buying a monitor that I can rotate to use as a vertical monitor?
Also, I want to know if someone is using it for program...
New

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

A few weeks ago I started using Warp a terminal written in rust. Though in it’s current state of development there are a few caveats (tab...
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

I’m able to do the “artistic” part of game-development; character designing/modeling, music, environment modeling, etc.
However, I don’t...
New

This is a very quick guide, you just need to:
Download LM Studio: https://lmstudio.ai/
Click on search
Type DeepSeek, then select the o...
New
Categories:
Sub Categories:
Popular Portals
- /elixir
- /rust
- /wasm
- /ruby
- /erlang
- /phoenix
- /keyboards
- /rails
- /js
- /python
- /security
- /go
- /swift
- /vim
- /clojure
- /java
- /haskell
- /emacs
- /svelte
- /onivim
- /typescript
- /crystal
- /c-plus-plus
- /tailwind
- /kotlin
- /gleam
- /react
- /flutter
- /elm
- /ocaml
- /ash
- /vscode
- /opensuse
- /centos
- /php
- /deepseek
- /html
- /scala
- /zig
- /debian
- /nixos
- /lisp
- /agda
- /textmate
- /react-native
- /sublime-text
- /kubuntu
- /arch-linux
- /revery
- /ubuntu
- /manjaro
- /spring
- /django
- /diversity
- /nodejs
- /lua
- /c
- /slackware
- /julia
- /neovim