Skip navigation links

valid8j 2.1.2 API

valid8j: A modern DbC library for Java.

See: Description

Packages 
Package Description
com.github.valid8j
This package collects entry point classes of the `valid8j` library.
com.github.valid8j.classic
A package for "classic" programming style of valid8j, where a value to be checked and a predicate with which the value is checked are passed to checking methods.
com.github.valid8j.fluent
This package hosts classes for "Fluent" programming model of valid8j.
com.github.valid8j.fluent.internals  
com.github.valid8j.metamor
A package that holds classes for "Metamorphic Testing".
com.github.valid8j.metamor.internals  
com.github.valid8j.pcond
A package to store classes for "printable conditions".
com.github.valid8j.pcond.core
A package to provide the core part of the pcond 's functionalities.
com.github.valid8j.pcond.core.fluent  
com.github.valid8j.pcond.core.fluent.builtins  
com.github.valid8j.pcond.core.identifieable
A package that provides a mechanism to make "identifieable" functions and predicates with at least one parameter for their creations.
com.github.valid8j.pcond.core.printable
A package that provides a mechanism to make functions and predicates (lambdas) printable.
com.github.valid8j.pcond.core.refl
Offers a mechanism to invoke a method dynamically.
com.github.valid8j.pcond.experimentals.currying
The currying package provides a mechanism to "curry" a multi-parameters function.
com.github.valid8j.pcond.experimentals.currying.context
A package to provide the "Context" mechanism of the pcond library.
com.github.valid8j.pcond.experimentals.currying.multi
A package to handle "multi-parameters" function.
com.github.valid8j.pcond.experimentals.cursor  
com.github.valid8j.pcond.fluent
A package to provide entry-point classes for the Fluent style.
com.github.valid8j.pcond.forms
This package stores entry-point classes to static import for your conveniences.
com.github.valid8j.pcond.internals
A package to place utility classes internally used by the pcond library.
com.github.valid8j.pcond.validator
A package that holds classes providing core functionality of the pcond package.
com.github.valid8j.pcond.validator.exceptions
A package to store exception classes thrown when a condition passed to assertion/validation method of pcond is not satisfied.

valid8j: A modern DbC library for Java.

The valid8j is a DbC (design by contract) programming supporting library for Java intended to be a successor of valid4j[1]. You can also use this library instead of Guava’s Preconditions class, Apache Common’s Validate class, and test assertion libraries such as Hamcrest[2], AssertJ[4] or Google Truth[5] with this.

Design Goals

Following is a list of goals that the pcond library tries to achieve.

  • Unified Support for Value Validation, DbC, Test Assertion programming: Input/status value checking, user input validation, Checks in Design by Contract, and test assertions have similar concerns, however, completely different solutions are provided [1]. The pcond tries to offer a unified solution to those use cases.

  • More Programmer-Friendly Readability and Writability: Test assertion libraries have more and more focused on writability of code from readability of code and output. The pcond makes one step further by addressing remaining pain points.

    • Easy to handle custom types: Test assertion libraries require users to implement custom value verifier (Matcher in Hamcrest, Assert in AssertJ, Subject in Google Truth) classes. This is a significant work by itself, and it may also impede refactorings of the software under test.

    • Remove repeating the same fact twice in the failure message and the predicate: More or less similar to Google Guava’s Preconditions class, a human needs to describe the condition to be checked twice. One is in a natural language to print what condition is violated. And the other is in Java programming language to define what check should be done for a given value. This is a practice that violates "D-R-Y" (Don’t repeat yourself) principle. This is more important for programmers than printing a nice message on a failure.

    • Remove fail→fix→run loop: In the earliest age of JUnit, there used not to be a good way to verify multiple conditions in one test method. Programmers needed to call assert, assertEquals, etc. multiple times from inside one test method. This was causing a situation, where they need to repeat fail→fix→run→fail→fix→run…​ loop, once an assert method call fails. Ensuring a good way to write a test method with only a single assertion method call is important.

    • Not having to go back-and-force between source code and output: When a check fails, making a full description of the failure available is important. Otherwise, you will need to go back-and-forth between the code and the log (or test report).

    • Human-analyzable output: A readable message is essential to understand what happened when a check fails. However, the pcond interprets it in a slightly different way from other existing assertion libraries. It focuses more on making a failure report self-sufficient and structured. Rather than making it look "natural" as if it were written by a human, it focuses on make it readable for programmers. Not for general English-speaking human.

  • Make custom verifiers composable: Instead of let a user define a custom verifier for every type, pcond offers a way to compose a verifier for a user type from already provided functions and predicates.

  • Configurable: There are customization points for a general purpose library like pcond. For instance a failure report format, an exception type for a combination of a certain context and a certain use case, A message template for a failure, etc. Providing a good way to customize these configurations at runtime is important.

Design

To achieve the goals, the pcond library equips the following mechanisms.

  • Predicates and function for value checking: Instead of custom verifier classes like Matcher, Assert, or Subject, use conventional`Function` and Predicate so that it can be used not only for test assertions but also for DbC, general value checking, etc.

    • Transform and Check mechanism: Decompose a verification process into two steps: "Transform" function and "Check" predicate.

    • Printable mechanism: Make functions and predicates "printable".

    • Identifiable mechanism: It’s preferable to be able to "identify" two predicates are equal, if they are meant to be the same.

  • Context and Currying mechanism: Sometimes we need to handle functions and predicates that have more than one parameter. The Context and Currying are designed for such functions.

  • "Fluent" style support mechanism: Nowadays, "fluent" style is more and more preferred in test assertions. pcond also supports the style.

  • Configurable ValueChecker mechanism: To use the library with various testing frameworks, pcond has a configurable mechanism called ValueChecker. You can configure it for your own testing framework. It supports JUnit4 and JUnit5 out-of-box.

"Transform and Check"

The most fundamental idea of the pcond library is to use Predicate s, not special classes like Matcher s (Hamcrest), AbstractAssert (AssertJ), or Subject (Google Truth).

A basis of the design of the pcond library is an observation that we almost always can convert a given value into a type, which is convenient for verification, and then we can do the final verification.

For instance, if we have a custom typed value, we can convert it to a string, for which we can think of a lot of ways to verify if the value matches our expectation and to report how it didn’t match the expectation.

In the step one, we transform a value into a type for which we have a method to verify it. Then we will check it with the intended method.

diag 7121b2a03252c003bc7b8be5cc37cae1
Figure 1. Value Verification in a Test

"Convenient" types for verification pcond chose are Object, String, List, and numbers (int, long, double, …​), as of now.

This entire step \$q\$ can be considered one Predicate for a value of type T. \$p\$ is a predicate for verification of a convenient type value. \$f\$ is a function to transform a given value to a convenient type.

\$q(v) = p(f(v))\$

For \$p\$, we only need to support a limited number of predicates because, programmers will always convert a given value into some handy types. So, we don’t need to come up with a nicely readable messages for every method you define in your custom matchers. Instead, the pcond library composes it from the transformation function \$f\$ and \$p\$ 's string representations.

Following is a small example code, which examines if a given value yourName satisfies required conditions.

public class ExampleDbC {
 public static void main(String[] args) {
   System.out.println(hello("JohnDoe"));
 }

 public static String hello(String yourName) {
   // (2)
   requireArgument(yourName, and(isNotNull(),
                                 transform(length()).check(gt(0)),
                                 containsString(" ")));
   String ret = String.format("Hello, %s", NameUtils.firstNameOf(yourName));
   // (3)
   return ensureNonNull(ret);
 }
}

This prints the following output.

Exception in thread "main" java.lang.IllegalArgumentException: value:<"JohnDoe"> violated precondition:value (isNotNull&&length >[0]&&containsString[" "])
"JohnDoe"->&&                   ->false
            isNotNull          ->true
            transform
              length           ->7
7        ->  check
              >[0]             ->true
"JohnDoe"->  containsString[" "]->false

It might not be 100% natural English text, but still very easily understandable for programmers. The author of the library believes it is more useful and reliable for developers.

Printable mechanism

To implement library like above, it’s necessary to format a predicate into a human-understandable format. Unfortunately, it is not sufficient and not straight forward to override the toString method. Because:

  1. In the "transform and check" style requires Function s, not only Predicate s.

  2. It is not possible to override toString method in an interface.

  3. Predicate and Function interfaces have a few methods that return a newly created Predicate and Function (Predicate#and, Function#compose, for instances ). These returned objects also need to have overridden version of toString.

  4. Overriding toString is a cumbersome manual task.

The pcond library provides a solution to these problems by offering its own base classes for Predicate and Function.

The implementation of this feature is provided by the com.github.valid8j.pcond.core.printable package.

Identifiable mechanism

The pcond has a mechanism to create a "parameterized" predicate, such as Predicates.containsString(String). If you call this method twice, two different predicate objects are returned. However, should those return make Objects.equals(Object,Object) return false?

class Example{
 public static void main(String... args) {
   Predicate<String> p1 = Predicates.containsString("hello");
   Predicate<String> p2 = Predicates.containsString("hello");
   System.out.println(p1.equals(p2));
 }
}

valid8j is designed to return true for this check.

The implementation of this feature is provided by the com.github.dakusui.valid8j.pcond.core.identifieable package.

Entry points

The packages com.github.valid8j.classic and com.github.valid8j.fluent hold entry point classes of the valid8j library.

(Test) Assertion Library

One usage of the pcond library is an assertion library for testing. It supports two styles. One is traditional "Hamcrest" like style and the other is more recently fashioned "fluent" style like AssertJ or Google Truth.

Hamcrest Style

Hamcrest [2] is the first popular assertion library. The style JUnit itself presented at the time Hamcrest was published is to call assertEquals, assertTrue, assertFalse methods. Those methods fail if the given value do not satisfy the desired condition. Also, they print human-readable message about what happened. That is, what the given value was and what was expected.

This approach leads to an explosion of the number of assertXyz methods because we need to verify values with a lot of different expectations and, for each of them, this approach requires one assertXyz method.

Hamcrest separated an assertion into two parts, one of which controls a value checking flow and the other is the part that defines a condition to be satisfied.

Following is an example found in Hamcrest’s tutorial[3]:

import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

public class BiscuitTest {
 @Test
 public void testEquals() {
   Biscuit theBiscuit = new Biscuit("Ginger");
   Biscuit myBiscuit = new Biscuit("Ginger");
   assertThat(theBiscuit, equalTo(myBiscuit)); // The Line
 }
}

The object returned by a static method Matchers.equalTo is a Matcher object as other static methods in the class do. The example verifies if theBuiscuit is equalTo myBiscuit as it says.

Suppose if myBiscuit is Sugar and this test fails, the following message will be printed:

java.lang.AssertionError:
Expected: <Sugar>
    but: was <Ginger>
Expected :<Sugar>
Actual   :<Ginger>

If we want to test a different expectation, for instance, suppose we want to check if the value is not equal when a different object is given to be compared. We can modify the test as follows at The Line:

   assertThat(theBiscuit, not(equalTo(myBiscuit))); // The Line

Thus, with Hamcrest, you can construct various conditions from (relatively) limited number of Matcher classes. Now you can write a human-readable test which prints a human-readable failure report.

However, there are still two remaining pain points:

  • To test your own class, you will need to implement a custom matcher class for better readability. This is not a straight forward task.

  • Hamcrest was designed and published at the age where Java 8 did not exist, which introduced lambda and Predicate. Neither using a matcher as a predicate nor the other way around is not straight forward, although it will be convenient if it is possible.

The approach pcond took is as follows.

  • Introduce the "Transform-and-check" concept to uniform the check. This will allow us to support our own class just by writing a printable function to convert the object to already fixed types.

  • Use Java’s out-of-box Predicate and Function for that.

Following is the simplest example of pcond style test.

public class UTExample {
 @Test
 public void shouldPass_testFirstNameOf() {
   String firstName = NameUtils.firstNameOf("Yoshihiko Naito");
   assertThat(firstName, allOf(not(containsString(" ")), startsWith("Y")));
 }
}

and, not, containsString, and startsWith are just predicates of Java. If you want to do a custom check, you can write your own predicate, as usual programming. If you watn to check your custom class, you can write your own function, which converts your custom value to well-known types such as String, Number, Boolean, List of them, etc., as usual programming. If the NameUtils.firstNameOf returns an empty string, it will print the following error message.

org.junit.ComparisonFailure: Value:"" violated: (!containsString[" "]&&startsWith["R"])
 ""->&&                     ->true |""->&&                     ->false
       !                    ->true |      !                    ->true
         containsString[" "]->false|        containsString[" "]->false
X      startsWith["Y"]      ->true |      startsWith["Y"]      ->false

For an equivalent test, what Hamcrest prints as an error report is:

java.lang.AssertionError:
Expected: (not a string containing " " and a string starting with "R")
    but: a string starting with "R" was ""

As you see, pcond gives more informative report. It shows each predicate’s expected actual predicate one by one and with a modern IDE, those will be shown side-by-side. You will notice the only last predicate startsWith["Y"] was not satisfied by the value "" and that’s why the test failed.

While you will need to analyze which part of the Expected was not satisfied by the input value "" and how by yourself from the Hamcrest’s report.

Fluent Style: Challenges in Existing Libraries

The next challenge assertion libraries faced was the explosion of the static methods to be imported. There is a bunch of static methods to be imported and classes to which they belong. Hamcrest itself has twenty-four matcher classes, each of for which entry point class is necessary. On top of that, there is a bunch of third party libraries.

What the author of AssertJ or Google Truth thought is to let programmers create a builder object first by a static method and then from the object, let programmers choose the next method to call using "fluent" style.

Following is the example for the usage of AssertJ based testing code:

class AssertJExample {
 public void assertJexample() {
   // AsssertJ example from:
   // - https://assertj.github.io/doc/#overview-what-is-assertj
   // in the examples below fellowshipOfTheRing is a List<TolkienCharacter>
   assertThat(fellowshipOfTheRing).hasSize(9)
                                  .contains(frodo, sam)
                                  .doesNotContain(sauron);
 }
}

Major drawback of this approach is.:

  • No clean way to verify multiple values.

  • Still users need to write their own assertion builder class (Assert in AssertJ, Subject in Google Truth)

  • Each builder class will need to have a number of methods. This is because a builder just can "add" a simple check by one method. No way to create a new one from existing ones.

Adding an explanation only to the first point as the other two are more or less obvious. When you need to do assertions for multiple values in AssertJ, a normal way to achieve it is following:

public static class AssertJMultiValueExample {
 public void assertjMultiValueExample() {
   // https://stackoverflow.com/questions/47397525/multiply-conditions-set-in-assertj-assertions
   SoftAssertions phoneBundle = new SoftAssertions();
   phoneBundle.assertThat("a").as("Phone 1").isEqualTo("a");
   phoneBundle.assertThat("b").as("Service bundle").endsWith("c");
   phoneBundle.assertAll();
 }
}

This is a bit verbose, and it will silently PASS, if you forget calling assertAll in the end.

Fluent Style: valid8j, the Solution

An example for the valid8j 's fluent style looks like the following:

public class MoreFluentExample {
 @Test
 public void checkTwoValues() {
   String s = "HI";
   List<String> strings = asList("HELLO", "WORLD");

   assertAll(
       that(s).asString()
         .function(TestUtils.stringToLowerCase())
         .satisfies()
         .equalTo("HI"),
       that(strings).asListOf((String)value())
         .satisfies()
         .containingElementsInOrder("hello", "world"));
 }
}

Entry-point methods such as assertAll and that and others are defined in Expectations class.

This leads to the following report.:

 "HI"             ->WHEN:treatAsString        ->"HI"
                      stringToLowerCase       ->"hi"
X"hi"             ->THEN:isEqualTo["HI"]      ->false
 ["HELLO","WORLD"]->WHEN:treatAsList          ->["HELLO","WORLD"]
                             :
                             :
 []               ->    (end)                 ->true

Thus, we can keep both the code and report human-readable.

As a Helper Library for Design by Contract programming

The valid8j can be used as a library for Design by Contract style programming in Java. There is a couple of ways for this usage.

With assert statement

With this style, you can benefit the good old feature of Java: assert.

public class Example {
 public void example(String arg) {
   assert precondition(arg, isNotNull().and(not(isEmpty())));
   System.out.println("Hello, " + arg + "!");
 }
}

You will see a human-readable and analyzable output, when an assertion fails. At the same time, once the assertion is disabled by the VM option -da, you will see no performance penalty for the feature. This style is useful in private methods, where you do not want to perform those checks in production.

Aside from the precondition method, that method is prepared for checking an invariant condition and postcondition method is prepared for what the name suggests.

With requireXyz and ensureXyz methods

You may want to force your code to conduct a check for input value.

public class Example {
 public static String hello(String yourName) {
   // (1)
   requireArgument(yourName, and(isNotNull(), transform(length()).check(gt(0)), containsString(" ")));
   String ret = String.format("Hello, %s", NameUtils.firstNameOf(yourName));
   // (2)
   return ensureNonNull(ret);
 }
}
  1. requireArgument method is defined in Requires class.

  2. ensureNonNull method is defined in Ensures class.

This approach useful for value checking in public methods, where you don’t want to accept illegal values in production. Since there is no way to disable it.

Fluent Style

Also for DbC programming and value validation, valid8j provides "fluent" programming style entry points. They can be found in Expectations class and they are:

  • precondition, preconditions, invariant, invariants, postcondition, and postconditions.

  • reqreuire, requireArgument, requireArguments, requireState, ensure, etc.

  • [1] Valid4J valid4j: 2015

  • [2] Hamcrest, Matchers that can be combined to create flexible expressions of intent Hamcrest: 2019

  • [3] Hamcrest, Hamcrest Tutorial Hamcrest Tutorial: 2019

  • [4] AssertJ, Fluent assertions for java AssertJ: 2022

  • [5] Truth - Fluent assertions for Java and Android Truth Truth: 2022

Enjoy.


1. Valid4J is the only example to the best knowledge of author of pcond library. It offers a style that unifies test assertions and DbC programming based on the Hamcrest library.
Skip navigation links

Copyright © 2024. All rights reserved.