diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8b337dd --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Shell script files must always be normalized +*.sh text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6120ac7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +#Maven +target/ + +#IntelliJ +/.idea +*.iml +*.ipr +*.iws +gradle-wrapper.jar + +#Eclipse +/.classpath +/.settings/ +/.project +/bin/ + +#Misc +*.log +.gradle \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8078268 --- /dev/null +++ b/pom.xml @@ -0,0 +1,276 @@ + + + 4.0.0 + + io.github.avegera + predicate4j + 0.1.0 + jar + + ${project.groupId}:${project.artifactId} + Human readable fluent API for predicates in Java + https://github.com/avegera/predicate4j + + + UTF-8 + + + 5.10.2 + 3.26.0 + + + 3.13.0 + 3.2.5 + 3.3.1 + 3.7.0 + 3.2.4 + 1.7.0 + 0.8.12 + + + + + The Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + avegera + Artem Vegera + https://github.com/avegera + + + + + scm:git:git://github.com/avegera/predicate4j.git + scm:git:ssh://github.com:avegera/predicate4j.git + https://github.com/avegera/predicate4j/tree/main + + + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots + + + + + + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit-jupiter.version} + test + + + org.assertj + assertj-core + ${asserj.version} + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + 8 + + + + attach-javadocs + + jar + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + + prepare-agent + + + + jacoco-check + + check + + + + + CLASS + + + LINE + COVEREDRATIO + 0.9 + + + METHOD + COVEREDRATIO + 0.9 + + + INSTRUCTION + COVEREDRATIO + 0.9 + + + BRANCH + COVEREDRATIO + 0.9 + + + COMPLEXITY + COVEREDRATIO + 0.9 + + + + + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + true + + ossrh + https://s01.oss.sonatype.org/ + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + java-code-coverage + + true + + + + + org.jacoco + jacoco-maven-plugin + + + + + + sign + + false + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + + + + release + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + + + + + \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/Predicates.java b/src/main/java/io/github/avegera/predicate4j/Predicates.java new file mode 100644 index 0000000..857e62a --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/Predicates.java @@ -0,0 +1,46 @@ +package io.github.avegera.predicate4j; + +import java.util.Objects; +import java.util.function.Predicate; + +/** + * This class contains library of common java predicates. + */ +public class Predicates { + + private Predicates() { + //private constructor + } + + public static Predicate isEqualTo(T object) { + return it -> Objects.equals(it, object); + } + + public static Predicate notEqualTo(T object) { + return it -> !Objects.equals(it, object); + } + + public static Predicate isNull() { + return Objects::isNull; + } + + public static Predicate notNull() { + return Objects::nonNull; + } + + public static Predicate isInstanceOf(Class clazz) { + return obj -> clazz != null && clazz.isInstance(obj); + } + + public static Predicate notInstanceOf(Class clazz) { + return obj -> clazz == null || !clazz.isInstance(obj); + } + + public static Predicate alwaysTrue() { + return it -> true; + } + + public static Predicate alwaysFalse() { + return it -> false; + } +} \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/Where.java b/src/main/java/io/github/avegera/predicate4j/Where.java new file mode 100644 index 0000000..0e6d7af --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/Where.java @@ -0,0 +1,17 @@ +package io.github.avegera.predicate4j; + +import io.github.avegera.predicate4j.api.WhereObject; +import io.github.avegera.predicate4j.impl.WhereObjectImpl; + +import java.util.function.Function; + +public class Where { + + private Where() { + //private constructor + } + + public static WhereObject where(Function mapper) { + return new WhereObjectImpl<>(mapper); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/api/RichPredicate.java b/src/main/java/io/github/avegera/predicate4j/api/RichPredicate.java new file mode 100644 index 0000000..3311fb4 --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/api/RichPredicate.java @@ -0,0 +1,6 @@ +package io.github.avegera.predicate4j.api; + +import java.util.function.Predicate; + +public interface RichPredicate extends Predicate { +} \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/api/WhereObject.java b/src/main/java/io/github/avegera/predicate4j/api/WhereObject.java new file mode 100644 index 0000000..8d87d45 --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/api/WhereObject.java @@ -0,0 +1,16 @@ +package io.github.avegera.predicate4j.api; + +public interface WhereObject { + + RichPredicate isEqualTo(R value); + + RichPredicate isInstanceOf(Class clazz); + + RichPredicate isNull(); + + RichPredicate notEqualTo(R value); + + RichPredicate notInstanceOf(Class clazz); + + RichPredicate notNull(); +} \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/impl/RichPredicateImpl.java b/src/main/java/io/github/avegera/predicate4j/impl/RichPredicateImpl.java new file mode 100644 index 0000000..a19d517 --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/impl/RichPredicateImpl.java @@ -0,0 +1,23 @@ +package io.github.avegera.predicate4j.impl; + +import io.github.avegera.predicate4j.api.RichPredicate; + +import java.util.function.Function; +import java.util.function.Predicate; + +public class RichPredicateImpl implements RichPredicate { + + private final Function mapper; + + private final Predicate predicate; + + public RichPredicateImpl(Function mapper, Predicate predicate) { + this.mapper = mapper; + this.predicate = predicate; + } + + @Override + public boolean test(T object) { + return predicate.test(mapper.apply(object)); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/avegera/predicate4j/impl/WhereObjectImpl.java b/src/main/java/io/github/avegera/predicate4j/impl/WhereObjectImpl.java new file mode 100644 index 0000000..19e7f0f --- /dev/null +++ b/src/main/java/io/github/avegera/predicate4j/impl/WhereObjectImpl.java @@ -0,0 +1,51 @@ +package io.github.avegera.predicate4j.impl; + +import io.github.avegera.predicate4j.Predicates; +import io.github.avegera.predicate4j.api.RichPredicate; +import io.github.avegera.predicate4j.api.WhereObject; + +import java.util.function.Function; +import java.util.function.Predicate; + +public class WhereObjectImpl implements WhereObject { + + private final Function mapper; + + public WhereObjectImpl(Function mapper) { + this.mapper = mapper; + } + + @Override + public RichPredicate isEqualTo(R value) { + return getPredicate(Predicates.isEqualTo(value)); + } + + @Override + public RichPredicate isInstanceOf(Class clazz) { + return getPredicate(Predicates.isInstanceOf(clazz)); + } + + @Override + public RichPredicate isNull() { + return getPredicate(Predicates.isNull()); + } + + @Override + public RichPredicate notEqualTo(R value) { + return getPredicate(Predicates.notEqualTo(value)); + } + + @Override + public RichPredicate notInstanceOf(Class clazz) { + return getPredicate(Predicates.notInstanceOf(clazz)); + } + + @Override + public RichPredicate notNull() { + return getPredicate(Predicates.notNull()); + } + + protected RichPredicate getPredicate(Predicate predicate) { + return new RichPredicateImpl<>(mapper, predicate); + } +} \ No newline at end of file diff --git a/src/test/java/io/github/avegera/predicate4j/PredicatesTest.java b/src/test/java/io/github/avegera/predicate4j/PredicatesTest.java new file mode 100644 index 0000000..cc6daf8 --- /dev/null +++ b/src/test/java/io/github/avegera/predicate4j/PredicatesTest.java @@ -0,0 +1,328 @@ +package io.github.avegera.predicate4j; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.function.Predicate; + +import static io.github.avegera.predicate4j.Predicates.*; +import static org.assertj.core.api.Assertions.assertThat; + +class PredicatesTest { + + @Nested + @DisplayName("Object predicate") + class ObjectPredicate { + + @Nested + @DisplayName("isEqualTo()") + class IsEqualTo { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is equal to provided object") + void objectIsEqualToProvidedObject() { + Object object = new Object(); + Predicate predicate = isEqualTo(object); + assertThat(predicate).accepts(object); + } + + @Test + @DisplayName("object is null and provided object is null") + void objectIsNullAndProvidedObjectIsNull() { + Predicate predicate = isEqualTo(null); + assertThat(predicate).accepts((Object) null); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is not equal to the provided object") + void objectIsNotEqualToTheProvidedObject() { + Object object = new Object(); + Predicate predicate = isEqualTo(object); + assertThat(predicate).rejects(new Object()); + } + + @Test + @DisplayName("object is null and provided object is not null") + void objectIsNullAndProvidedObjectIsNotNull() { + Predicate predicate = isEqualTo(null); + assertThat(predicate).rejects(new Object()); + } + + @Test + @DisplayName("object is not null and provided object is null") + void objectIsNotNullAndProvidedObjectIsNull() { + Predicate predicate = isEqualTo(new Object()); + assertThat(predicate).rejects((Object) null); + } + } + } + + @Nested + @DisplayName("notEqualTo()") + class NotEqualTo { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is not equal to the provided object") + void objectIsNotEqualToTheProvidedObject() { + Object object = new Object(); + Predicate predicate = notEqualTo(object); + assertThat(predicate).accepts(new Object()); + } + + @Test + @DisplayName("object is null and provided object is not null") + void objectIsNullAndProvidedObjectIsNotNull() { + Predicate predicate = notEqualTo(new Object()); + assertThat(predicate).accepts((Object) null); + } + + @Test + @DisplayName("object is not null and provided object is null") + void objectIsNotNullAndProvidedObjectIsNull() { + Predicate predicate = notEqualTo(null); + assertThat(predicate).accepts(new Object()); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is equal to provided object") + void objectIsEqualToProvidedObject() { + Object object = new Object(); + Predicate predicate = notEqualTo(object); + assertThat(predicate).rejects(object); + } + + @Test + @DisplayName("object is null and provided object is null") + void objectIsnullAndProvidedObjectIsNull() { + Predicate predicate = notEqualTo(null); + assertThat(predicate).rejects((Object) null); + } + } + } + + @Nested + @DisplayName("isNull()") + class IsNull { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is null") + void objectIsNull() { + Predicate predicate = isNull(); + assertThat(predicate).accepts((Object) null); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is not null") + void objectIsNotNull() { + Predicate predicate = isNull(); + assertThat(predicate).rejects(new Object()); + } + } + } + + @Nested + @DisplayName("notNull()") + class NotNull { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is not null") + void objectIsNotNull() { + Predicate predicate = notNull(); + assertThat(predicate).accepts(new Object()); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is null") + void objectIsNull() { + Predicate predicate = notNull(); + assertThat(predicate).rejects((Object) null); + } + } + } + + @Nested + @DisplayName("isInstanceOf()") + class IsInstanceOf { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is an instance of the provided class") + void objectIsAnInstanceOfTheProvidedClass() { + Predicate predicate = isInstanceOf(String.class); + assertThat(predicate).accepts("test string"); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is not an instance of the provided class") + void objectIsNotAnInstanceOfTheProvidedClass() { + Predicate predicate = isInstanceOf(String.class); + assertThat(predicate).rejects(123); + } + + @Test + @DisplayName("class is null") + void classIsNull() { + Predicate predicate = isInstanceOf(null); + assertThat(predicate).rejects("test string"); + } + } + } + + @Nested + @DisplayName("notInstanceOf()") + class NotInstanceOf { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is not an instance of the provided class") + void objectIsNotAnInstanceOfTheProvidedClass() { + Predicate predicate = notInstanceOf(String.class); + assertThat(predicate).accepts(123); + } + + @Test + @DisplayName("class is null") + void classIsNull() { + Predicate predicate = notInstanceOf(null); + assertThat(predicate).accepts("test string"); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is an instance of the provided class") + void objectIsAnInstanceOfTheProvidedClass() { + Predicate predicate = notInstanceOf(String.class); + assertThat(predicate).rejects("test string"); + } + } + } + + @Nested + @DisplayName("alwaysTrue()") + class AlwaysTrue { + + @Nested + @DisplayName("returns true when") + class ReturnsTrue { + + @Test + @DisplayName("object is null") + void objectIsNull() { + Predicate predicate = alwaysTrue(); + assertThat(predicate).accepts((Object) null); + } + + @Test + @DisplayName("object is non-null") + void objectIsNonNull() { + Predicate predicate = alwaysTrue(); + assertThat(predicate).accepts(new Object()); + } + + @Test + @DisplayName("object is a string") + void objectIsString() { + Predicate predicate = alwaysTrue(); + assertThat(predicate).accepts("test string"); + } + + @Test + @DisplayName("object is an integer") + void objectIsInteger() { + Predicate predicate = alwaysTrue(); + assertThat(predicate).accepts(123); + } + } + } + + @Nested + @DisplayName("alwaysFalse()") + class AlwaysFalse { + + @Nested + @DisplayName("returns false when") + class ReturnsFalse { + + @Test + @DisplayName("object is null") + void objectIsNull() { + Predicate predicate = alwaysFalse(); + assertThat(predicate).rejects((Object) null); + } + + @Test + @DisplayName("object is non-null") + void objectIsNonNull() { + Predicate predicate = alwaysFalse(); + assertThat(predicate).rejects(new Object()); + } + + @Test + @DisplayName("object is a string") + void objectIsString() { + Predicate predicate = alwaysFalse(); + assertThat(predicate).rejects("test string"); + } + + @Test + @DisplayName("object is an integer") + void objectIsInteger() { + Predicate predicate = alwaysFalse(); + assertThat(predicate).rejects(123); + } + } + } + } +} \ No newline at end of file diff --git a/src/test/java/io/github/avegera/predicate4j/WhereTest.java b/src/test/java/io/github/avegera/predicate4j/WhereTest.java new file mode 100644 index 0000000..1ad8639 --- /dev/null +++ b/src/test/java/io/github/avegera/predicate4j/WhereTest.java @@ -0,0 +1,293 @@ +package io.github.avegera.predicate4j; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.function.Predicate; + +import static io.github.avegera.predicate4j.Where.where; +import static org.assertj.core.api.Assertions.assertThat; + +class WhereTest { + + private static final int TEST_ID_1 = 123; + + private static final int TEST_ID_2 = 456; + + @Nested + @DisplayName("Predicate from method where(mapper)") + class WhereObjectImpl { + + @Nested + @DisplayName("and method isEqualTo(value)") + class IsEqualTo { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is equal to provided value") + void mappedValueIsEqualToProvidedValue() { + Predicate predicate = where(User::id).isEqualTo(TEST_ID_1); + assertThat(predicate).accepts(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided value is null") + void mappedValueIsNullAndProvidedValueIsNull() { + Predicate predicate = where(User::id).isEqualTo(null); + assertThat(predicate).accepts(new User(null)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is not equal to provided value") + void mappedValueIsNotEqualToProvidedValue() { + Predicate predicate = where(User::id).isEqualTo(TEST_ID_1); + assertThat(predicate).rejects(new User(TEST_ID_2)); + } + + @Test + @DisplayName("mapped value is not null and provided value is null") + void mappedValueIsNotNullAndProvidedValueIsNull() { + Predicate predicate = where(User::id).isEqualTo(null); + assertThat(predicate).rejects(new User(TEST_ID_2)); + } + + @Test + @DisplayName("mapped value is null and provided value is not null") + void mappedValueIsNullAndProvidedValueIsNotNull() { + Predicate predicate = where(User::id).isEqualTo(TEST_ID_1); + assertThat(predicate).rejects(new User(null)); + } + } + } + + @Nested + @DisplayName("and method notEqualTo(value)") + class NotEqualTo { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is not equal to provided value") + void mappedValueIsNotEqualToProvidedValue() { + Predicate predicate = where(User::id).notEqualTo(TEST_ID_1); + assertThat(predicate).accepts(new User(TEST_ID_2)); + } + + @Test + @DisplayName("mapped value is not null and provided value is null") + void mappedValueIsNotNullAndProvidedValueIsNull() { + Predicate predicate = where(User::id).notEqualTo(null); + assertThat(predicate).accepts(new User(TEST_ID_2)); + } + + @Test + @DisplayName("mapped value is null and provided value is not null") + void mappedValueIsNullAndProvidedValueIsNotNull() { + Predicate predicate = where(User::id).notEqualTo(TEST_ID_1); + assertThat(predicate).accepts(new User(null)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is equal to provided value") + void mappedValueIsEqualToProvidedValue() { + Predicate predicate = where(User::id).notEqualTo(TEST_ID_1); + assertThat(predicate).rejects(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided value is null") + void mappedValueIsNullAndProvidedValueIsNull() { + Predicate predicate = where(User::id).notEqualTo(null); + assertThat(predicate).rejects(new User(null)); + } + } + } + + @Nested + @DisplayName("and method isNull()") + class IsNull { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is null") + void mappedValueIsNull() { + Predicate predicate = where(User::id).isNull(); + assertThat(predicate).accepts(new User(null)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is not null") + void mappedValueIsNotNull() { + Predicate predicate = where(User::id).isNull(); + assertThat(predicate).rejects(new User(TEST_ID_1)); + } + } + } + + @Nested + @DisplayName("and method notNull()") + class NotNull { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is not null") + void mappedValueIsNotNull() { + Predicate predicate = where(User::id).notNull(); + assertThat(predicate).accepts(new User(TEST_ID_1)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is null") + void mappedValueIsNull() { + Predicate predicate = where(User::id).notNull(); + assertThat(predicate).rejects(new User(null)); + } + } + } + + @Nested + @DisplayName("and method isInstanceOf(clazz)") + class IsInstanceOf { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is instance of provided class") + void mappedValueIsInstanceOfProvidedClass() { + Predicate predicate = where(User::id).isInstanceOf(Integer.class); + assertThat(predicate).accepts(new User(TEST_ID_1)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is not instance of provided class") + void mappedValueIsNotInstanceOfProvidedClass() { + Predicate predicate = where(User::id).isInstanceOf(String.class); + assertThat(predicate).rejects(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided class is not null") + void mappedValueIsNullAndProvidedClassIsNotNull() { + Predicate predicate = where(User::id).isInstanceOf(Integer.class); + assertThat(predicate).rejects(new User(null)); + } + + @Test + @DisplayName("mapped value is not null and provided class is null") + void providedClassIsNull() { + Predicate predicate = where(User::id).isInstanceOf(null); + assertThat(predicate).rejects(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided class is null") + void mappedValueIsNullAndProvidedClassIsNull() { + Predicate predicate = where(User::id).isInstanceOf(null); + assertThat(predicate).rejects(new User(null)); + } + } + } + + @Nested + @DisplayName("and method notInstanceOf(clazz)") + class NotInstanceOf { + + @Nested + @DisplayName("returns true when") + class ReturnsTrueWhen { + + @Test + @DisplayName("mapped value is not instance of provided class") + void mappedValueIsNotInstanceOfProvidedClass() { + Predicate predicate = where(User::id).notInstanceOf(String.class); + assertThat(predicate).accepts(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided class is not null") + void mappedValueIsNullAndProvidedClassIsNotNull() { + Predicate predicate = where(User::id).notInstanceOf(Integer.class); + assertThat(predicate).accepts(new User(null)); + } + + @Test + @DisplayName("mapped value is not null and provided class is null") + void providedClassIsNull() { + Predicate predicate = where(User::id).notInstanceOf(null); + assertThat(predicate).accepts(new User(TEST_ID_1)); + } + + @Test + @DisplayName("mapped value is null and provided class is null") + void mappedValueIsNullAndProvidedClassIsNull() { + Predicate predicate = where(User::id).notInstanceOf(null); + assertThat(predicate).accepts(new User(null)); + } + } + + @Nested + @DisplayName("returns false when") + class ReturnsFalseWhen { + + @Test + @DisplayName("mapped value is instance of provided class") + void mappedValueIsInstanceOfProvidedClass() { + Predicate predicate = where(User::id).notInstanceOf(Integer.class); + assertThat(predicate).rejects(new User(TEST_ID_1)); + } + } + } + } +} + +class User { + + private final Integer id; + + public User(Integer id) { + this.id = id; + } + + public Integer id() { + return id; + } +} \ No newline at end of file