Generated from Example_Validations_Fluent_Test.java at 3.0.0 2020-08-11T17:36:18Z

Fluent Validations

Abstract

Basic introduction (by example) to fluent validations with library functions. Described here functionality is available since version 3.0.0.

Examples

Before you read more please read the article about the simple validations with predicates, that explains premise of X Be, Does, Is, P.

Introduced here examples cover few most common cases of validation/assertions.

Before moving further, please note that various factory methods represents different verbosity and exception being thrown.

Lets consider very simple validation that need to be done.


@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Argument \\[\\?\\]: #3 cannot be greater or equal 50") public void test1() { Checks.arg(arg45) .mustNot2(Be::gtEq, 50, "#1 cannot be greater or equal 50") //passes .must2(Be::lt, 50, "#2 cannot be lighter than 50"); //passes var i = arg(60) .mustNot2(Be::gtEq, 50, "#3 cannot be greater or equal 50") //fails .get(); }

Now lets include the value and name of the argument to the message.


@Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "State \\[number=='60'\\]: cannot be greater or equal 40") public void test2() throws Exception { //fails var i = state(60, "number").verbosity(VAL).mustNot2(Be::gtEq, 40, "cannot be greater or equal 40").get(); }

Another good example:


@Test(expectedExceptions = IllegalValueException.class, expectedExceptionsMessageRegExp = "Value \\[arg45\\]: must be 5 < V=45 < 40") public void test3() { //fails value(arg45, "arg45").must3(Be::between, 5, 40, "must be %2$d < V=%1$d < %3$d"); }

Example with values derived from the object:


@Test(expectedExceptions = IllegalValueException.class, expectedExceptionsMessageRegExp = "Check \\[\\?\\]: Collection size must be >1") public void test4() { value(Collections.singleton(2), "collection name") .mustNot(Be::Null, "This collection cannot be null.") //fails: .checkInt(Collection::size, v -> v.must2(Be::gtEq, 2, "Collection size must be >1")); }

Similar but with derivative's name being used:


@Test(expectedExceptions = IllegalValueException.class, expectedExceptionsMessageRegExp = "Check \\[size\\]: Collection size must be >1") public void test5() { value(Collections.singleton(2), "collection_name") .mustNot(Be::Null, "This collection cannot be null.") //fails: .checkInt(Collection::size, "size", v -> v.must2(Be::gtEq, 2, "Collection size must be >1")); }

Another good example (the 'attest' method implies verbosity 'ALL', and AssertionError being thrown):


@Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "Check/attest \\[arg45=='45'\\]\\(params: '5', '40'\\): must be 5 < V=45 < 40") public void test6() { //fails attest(arg45, "arg45").must3(Be::between, 5, 40, "must be %2$d < V=%1$d < %3$d"); }

We can do the same without using 'attest' method (changing verbosity and exception factory):


@Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "Value \\[arg45=='45'\\]\\(params: '5', '40'\\): not between") public void test7() { //fails value(arg45, "arg45", AssertionError::new).verbosity(ALL).must3(Be::between, 5, 40, "not between"); }

Going back to the initial example, lets now use self-explaining variants of the methods 'Ex' (or simply for EXtended):


@Test(expectedExceptions = IllegalArgumentException.class, expectedExceptionsMessageRegExp = "Argument \\[\\?\\]: 60 must NOT be >= 50\\. - #3 some additional info: it failed") public void test1_bis() { Checks.arg(arg45) .must2Ex(Be::notGtEqEx, 50 ) //passes (there is no mustNotEx method for logical reasons) .must2Ex(Be::ltEx, 50); //passes var i = arg(60) .must2Ex(Be::notGtEqEx, 50, "#3 some additional info: %s", "it failed") //fails .get(); }

Alternatively you can build predicate with P.have (or Does.have) methods.


@Test public void test8() { attest(new Integer(1)) .must(P.have(Object::toString, P::notNull), "serialise to non-null string") .must(P.have(Object::toString, P::equal, "1"), "serialize to '1'") .must(P.haveToInt(Integer::intValue, P::equal, 1), "not between"); }

And in such cases you can also use self-explaining methods ('Ex').


@Test(expectedExceptions = AssertionError.class, expectedExceptionsMessageRegExp = "Check/attest \\[\\?=='1'\\]: 1 must be equal to 45.") public void test9() { attest(new Integer(1)) .mustEx(P.haveToIntEx(Integer::intValue, P::equalEx, 45)); }

Summary note on naming convention:

  • mustEx - Ex stands for self-explaining
  • must2 - 2 solves problem with ambiguous arguments (a matter of number of function arguments vs number of message arguments)

Generated from Example_Validations_Fluent_Test.java at 3.0.0 2020-08-11T17:36:18Z