• Stars
    star
    156
  • Rank 239,589 (Top 5 %)
  • Language
    Java
  • Created over 9 years ago
  • Updated about 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A tiny framework that makes it easy to write Test Data Builders in Java

A tiny framework that makes it easy to write Test Data Builders in Java

Build Status

Test Data Builders are described in the book Growing Object-Oriented Software, Guided by Tests by Steve Freeman and Nat Pryce. This library lets you write Test Data Builders with much less duplication and boilerplate code than the approach described in the book.

Download

You can download from Maven Central with the artifact coordinates:

com.natpryce:make-it-easy:4.0.0

Example

Consider the following class hierarchy. This hierarchy illustrates a couple of complicating factors: there is an abstract base class and there is a property (Fruit.ripeness) that is not set via the constructor but by an operation of the Fruit class.

public abstract class Fruit {
    private double ripeness = 0.0;

    public void ripen(double amount) {
        ripeness = Math.min(1.0, ripeness+amount);
    }

    public boolean isRipe() {
        return ripeness == 1.0;
    }
}

public class Apple extends Fruit {
    private int leaves;

    public Apple(int leaves) {
        this.leaves = leaves;
    }

    public int numberOfLeaves() {
        return leaves;
    }
}

public class Banana extends Fruit {
    public final double curve;

    public Banana(double curve) {
        this.curve = curve;
    }

    public double curve() {
        return curve;
    }
}

You can define Test Data Builders for Apples and Bananas with Make It Easy as follows:

public class FruitMakers {
    public static final Property<Fruit,Double> ripeness = newProperty();

    public static final Property<Apple, Integer> leaves = newProperty();

    public static final Property<Banana,Double> curve = newProperty();

    public static final Instantiator<Apple> Apple = new Instantiator<Apple>() {
        @Override public Apple instantiate(PropertyLookup<Apple> lookup) {
            Apple apple = new Apple(lookup.valueOf(leaves, 2));
            apple.ripen(lookup.valueOf(ripeness, 0.0));
            return apple;
        }
    };

    public static final Instantiator<Banana> Banana = new Instantiator<Banana>() {
        @Override public Banana instantiate(PropertyLookup<Banana> lookup) {
            Banana banana = new Banana(lookup.valueOf(curve, 0.5));
            banana.ripen(lookup.valueOf(ripeness, 0.0));
            return banana;
        }
    };
}

And use them like this:

Maker<Apple> appleWith2Leaves = an(Apple, with(2, leaves));
Maker<Apple> ripeApple = appleWith2Leaves.but(with(ripeness, 0.9));
Maker<Apple> unripeApple = appleWith2Leaves.but(with(ripeness, 0.125));

Apple apple1 = make(ripeApple);
Apple apple2 = make(unripeApple);

Banana defaultBanana = make(a(Banana));
Banana straightBanana = make(a(Banana, with(curve, 0.0)));
Banana squishyBanana = make(a(Banana, with(ripeness, 1.0)));

In contrast, doing so in the style documented in Growing Object-Oriented Software, Guided by Tests would look like this:

public interface Builder<T> {
    T build();
}

public class AppleBuilder implements Builder<Apple> {
    private double ripeness = 0.0;
    private int leaves = 1;

    private AppleBuilder() {}

    public static AppleBuilder anApple() {
        return new AppleBuilder();
    }

    public Apple build() {
        Apple apple = new Apple(leaves);
        apple.ripen(ripeness);
        return apple;
    }

    public AppleBuilder withRipeness(double ripeness){
        this.ripeness = ripeness;
        return this;
    }

    public AppleBuilder withLeaves(int leaves) {
        this.leaves = leaves;
        return this;
    }

    public AppleBuilder but() {
        return new AppleBuilder()
                .withRipeness(ripeness)
                .withLeaves(leaves);
    }
}

public class BananaBuilder implements Builder<Banana> {
    private double ripeness = 0.0;
    private double curve = 0.5;

    private BananaBuilder() {}

    public static BananaBuilder aBanana() {
        return new BananaBuilder();
    }

    public Banana build() {
        Banana apple = new Banana(curve);
        apple.ripen(ripeness);
        return apple;
    }

    public BananaBuilder withRipeness(double ripeness){
        this.ripeness = ripeness;
        return this;
    }

    public BananaBuilder withCurve(double curve) {
        this.curve = curve;
        return this;
    }

    public BananaBuilder but() {
        return new BananaBuilder()
                .withRipeness(ripeness)
                .withCurve(curve);
    }
}

And be used like this:

AppleBuilder appleWith2Leaves = anApple().withLeaves(2);
AppleBuilder ripeApple = appleWith2Leaves.but().withRipeness(0.9);
AppleBuilder unripeApple = appleWith2Leaves.but().withRipeness(0.125);

Apple apple1 = ripeApple.build();
Apple apple2 = unripeApple.build();

Banana defaultBanana = aBanana().build();
Banana straightBanana = aBanana().withCurve(0.0).build();
Banana squishyBanana = aBanana().withRipeness(1.0).build();

As you can see, with Make It Easy you have to write a lot less duplicated and boilerplate code. What duplication there is - in the declaration of anonymous Instantiator classes, for example - can be automatically inserted and refactored by modern IDEs. (You could also factor out calls to Fruit.ripen to a private helper method, but I left them duplicated for clarity.)

The full code for this example is in the Make It Easy repository.

More Repositories

1

adr-tools

Command-line tools for working with Architecture Decision Records
Shell
4,051
star
2

hamkrest

Hamcrest for Kotlin
Kotlin
344
star
3

konfig

Simple config properties API for Kotlin
Kotlin
275
star
4

snodge

Randomly mutate JSON, XML, HTML forms, text and binary data for fuzz testing
Kotlin
154
star
5

rusty-pi

Rust
144
star
6

pottery

It's like Twitter, for recording things that happen in your project, stored in it's version control repository.
Shell
93
star
7

goos-code-examples

Code examples from later chapters of Growing Object-Oriented Software, Guided by Tests
Java
70
star
8

maybe-java

A Maybe type for Java
Java
67
star
9

code-words

Extract individual (natural-language) words from source code
Shell
62
star
10

deft

Easy Distributed Feature Tracking
Python
60
star
11

higher-order-react-components-demo

A demonstration of higher-order React components
JavaScript
37
star
12

codea-controllers

A small library of composable multi-touch controllers for Codea that implement various game control mechanisms
Lua
26
star
13

krouton

Typesafe, compositional routing and reverse routing for web apps and HTTP microservices
Kotlin
25
star
14

python-parallelize

Simple fork/join parallelism with Python's for loop
Python
21
star
15

python-factcheck

A simple but extensible implementation of QuickCheck for Python 2.7 and Python 3 that works well with Pytest.
Python
17
star
16

learn-you-a-kotlin

Exercises for the tutorial "Learn You a Kotlin For All The Good It Will Do You"
Java
16
star
17

code-guide

Generate step-by-step explanations of code from special comments
JavaScript
16
star
18

codea-planet-battle

A simple multiplayer game for Codea
Lua
14
star
19

property-driven-diamond-kata

The "Diamond" kata, implemented by TDD with only property-based tests
Scala
12
star
20

worktorule

Manage test lifecycle by correlating failures with contents of an issue tracker.
Java
12
star
21

tdd-as-if-you-meant-it-at-agile-cambridge-2011

TDD as if You Meant it at Agile Cambridge 2011
Python
7
star
22

codea-fishing

A Simple Fishing Game
Lua
6
star
23

robots

A simple, cooperative game to teach the basics of programming
Kotlin
6
star
24

courseware

XML document type and tool chain for creating training courses
XSLT
6
star
25

codea-gui

A simple windowing system for writing GUIs in Codea
Lua
5
star
26

asciidoc-book-skeleton

A skeleton project for writing a book in Asciidoc format
Makefile
4
star
27

xsltest

XSLTest: XSLT easy stylesheet testing
4
star
28

calcpad

A utility for doing calculations as if with a pad and calculator: more useful than a calculator application, simpler than a spreadsheet
Python
4
star
29

courseware-html

Create elegant HTML presentations from Courseware documents
JavaScript
4
star
30

reservoir-rs

Reservoir sampling (Algorithm R) implemented in Rust
Rust
3
star
31

hamcrest-text-patterns

Automatically exported from code.google.com/p/hamcrest-text-patterns
Java
2
star
32

codea-bitmap-font

Bitmap Font Rendering for Codea
Lua
2
star
33

hamcrest-reflection

Automatically exported from code.google.com/p/hamcrest-reflection
Java
2
star
34

kotlin-lazy-seq

Kotlin
1
star
35

kotlinconf-2019-error-handling-examples

Kotlin
1
star
36

pottery-idea-plugin

A plugin for JetBrains IDEs for viewing and recording project history in Pottery format
Kotlin
1
star
37

spline-editor

Editor for Catmull-Rom splines, written in Kotlin with Jetpack Compose
Kotlin
1
star
38

budge

An old puzzle game written for Tcl/Tk.
JavaScript
1
star
39

jnirn

Generate C code that calls RegisterNatives for JNI code
Java
1
star