DATACMNS-810 - Refactor Query by Example API.

Split Example and ExampleSpec to create reusable components. Refactor builder pattern to a fluent API that creates immutable instances. Split user and framework API, Example and ExampleSpec are user API, created ExampleSpecAccessor for modules to access example spec configuration. Create static methods in GenericPropertyMatchers to ease creation of matchers in a readable style. Convert PropertySpecifier to inner class  and move PropertySpecifiers to ExampleSpec.

Related tickets: DATAJPA-218, DATAMONGO-1245.
Original pull request: #153.
This commit is contained in:
Mark Paluch
2016-02-25 09:31:24 +01:00
committed by Oliver Gierke
parent 4092f0716f
commit b0ce7009e8
12 changed files with 2792 additions and 907 deletions

View File

@@ -0,0 +1,220 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.ExampleSpec.NullHandler;
import org.springframework.data.domain.ExampleSpec.StringMatcher;
/**
* Unit test for {@link ExampleSpec}.
*
* @author Mark Paluch
* @soundtrack K2 - Der Berg Ruft (Club Mix)
*/
public class ExampleSpecUnitTests {
ExampleSpec exampleSpec;
@Before
public void setUp() throws Exception {
exampleSpec = ExampleSpec.untyped();
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnDefault() throws Exception {
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.DEFAULT));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnContainingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.untyped().withStringMatcherContaining();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.CONTAINING));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnStartingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.untyped().withStringMatcherStarting();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.STARTING));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnEndingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.untyped().withStringMatcherEnding();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.ENDING));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnFalseByDefault() throws Exception {
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(false));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsIsEmptyByDefault() throws Exception {
assertThat(exampleSpec.getIgnoredPaths(), is(empty()));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnIgnoreByDefault() throws Exception {
assertThat(exampleSpec.getNullHandler(), is(NullHandler.IGNORE));
}
/**
* @see DATACMNS-810
*/
@Test(expected = UnsupportedOperationException.class)
public void ignoredPathsIsNotModifiable() throws Exception {
exampleSpec.getIgnoredPaths().add("¯\\_(ツ)_/¯");
}
/**
* @see DATACMNS-810
*/
@Test(expected = IllegalArgumentException.class)
public void defaultExampleSpecWithoutTypeFails() throws Exception {
ExampleSpec.typed(null);
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnTrueWhenIgnoreCaseEnabled() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnoreCase();
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnTrueWhenIgnoreCaseSet() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnoreCase(true);
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnInclude() throws Exception {
exampleSpec = ExampleSpec.untyped().withIncludeNullValues();
assertThat(exampleSpec.getNullHandler(), is(NullHandler.INCLUDE));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnIgnore() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnoreNullValues();
assertThat(exampleSpec.getNullHandler(), is(NullHandler.IGNORE));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnConfiguredValue() throws Exception {
exampleSpec = ExampleSpec.untyped().withNullHandler(NullHandler.INCLUDE);
assertThat(exampleSpec.getNullHandler(), is(NullHandler.INCLUDE));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsShouldReturnCorrectProperties() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnorePaths("foo", "bar", "baz");
assertThat(exampleSpec.getIgnoredPaths(), contains("foo", "bar", "baz"));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(3));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsShouldReturnUniqueProperties() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnorePaths("foo", "bar", "foo");
assertThat(exampleSpec.getIgnoredPaths(), contains("foo", "bar"));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(2));
}
/**
* @see DATACMNS-810
*/
@Test
public void withCreatesNewInstance() throws Exception {
exampleSpec = ExampleSpec.untyped().withIgnorePaths("foo", "bar", "foo");
ExampleSpec configuredExampleSpec = exampleSpec.withIgnoreCase();
assertThat(exampleSpec, is(not(sameInstance(configuredExampleSpec))));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(2));
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(false));
assertThat(configuredExampleSpec.getIgnoredPaths(), hasSize(2));
assertThat(configuredExampleSpec.isIgnoreCaseEnabled(), is(true));
}
static class Person {
String firstname;
}
}

View File

@@ -15,24 +15,18 @@
*/
package org.springframework.data.domain;
import static org.hamcrest.collection.IsEmptyCollection.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsCollectionContaining.*;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.junit.Assert.*;
import static org.springframework.data.domain.Example.*;
import static org.springframework.data.domain.PropertySpecifier.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Example.NullHandler;
import org.springframework.data.domain.Example.StringMatcher;
import org.springframework.data.domain.PropertySpecifier.NoOpPropertyValueTransformer;
import org.springframework.data.domain.PropertySpecifier.PropertyValueTransformer;
/**
* Test for {@link Example}.
*
* @author Christoph Strobl
* @author Mark Paluch
*/
public class ExampleUnitTests {
@@ -45,7 +39,7 @@ public class ExampleUnitTests {
person = new Person();
person.firstname = "rand";
example = exampleOf(person);
example = of(person);
}
/**
@@ -53,196 +47,7 @@ public class ExampleUnitTests {
*/
@Test(expected = IllegalArgumentException.class)
public void exampleOfNullThrowsException() {
new Example<Object>(null);
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldUseDefaultStringMatcher() {
assertThat(example.getDefaultStringMatcher(), equalTo(StringMatcher.DEFAULT));
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldUseDefaultStringMatcherForPathThatDoesNotHavePropertySpecifier() {
assertThat(example.getStringMatcherForPath("firstname"), equalTo(example.getDefaultStringMatcher()));
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldUseConfiguredStringMatcherAsDefaultForPathThatDoesNotHavePropertySpecifier() {
example = newExampleOf(person).withStringMatcher(StringMatcher.CONTAINING).get();
assertThat(example.getDefaultStringMatcher(), equalTo(StringMatcher.CONTAINING));
assertThat(example.getStringMatcherForPath("firstname"), equalTo(StringMatcher.CONTAINING));
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldUseDefinedStringMatcherForPathThatDoesHavePropertySpecifierWithStringMatcher() {
example = newExampleOf(person).withPropertySpecifier(
PropertySpecifier.newPropertySpecifier("firstname").matchString(StringMatcher.CONTAINING).get()).get();
assertThat(example.getStringMatcherForPath("firstname"), equalTo(StringMatcher.CONTAINING));
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldFavorStringMatcherDefinedForPathOverConfiguredDefaultStringMatcher() {
example = newExampleOf(person)
.withStringMatcher(StringMatcher.STARTING)
.withPropertySpecifier(
PropertySpecifier.newPropertySpecifier("firstname").matchString(StringMatcher.CONTAINING).get()).get();
assertThat(example.getDefaultStringMatcher(), equalTo(StringMatcher.STARTING));
assertThat(example.getStringMatcherForPath("firstname"), equalTo(StringMatcher.CONTAINING));
}
/**
* @see DATACMNS-810
*/
@Test
public void exampleShouldUseDefaultStringMatcherForPathThatHasPropertySpecifierWithoutStringMatcher() {
example = newExampleOf(person).withStringMatcher(StringMatcher.STARTING)
.withPropertySpecifier(ignoreCase("firstname")).get();
assertThat(example.getStringMatcherForPath("firstname"), equalTo(StringMatcher.STARTING));
}
/**
* @see DATACMNS-810
*/
@Test
public void isIgnoredPathShouldReturnFalseWhenNoPathsIgnored() {
assertThat(example.getIgnoredPaths(), is(empty()));
assertThat(example.isIgnoredPath("firstname"), is(false));
}
/**
* @see DATACMNS-810
*/
@Test
public void isIgnoredPathShouldReturnTrueWhenNoPathsIgnored() {
example = newExampleOf(person).ignore("firstname").get();
assertThat(example.getIgnoredPaths(), hasItem("firstname"));
assertThat(example.isIgnoredPath("firstname"), is(true));
}
/**
* @see DATACMNS-810
*/
@Test(expected = UnsupportedOperationException.class)
public void ignoredPathsShouldNotAllowModification() {
example.getIgnoredPaths().add("o_O");
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnFalseByDefault() {
assertThat(example.isIngnoreCaseEnabled(), is(false));
assertThat(example.isIgnoreCaseForPath("firstname"), is(false));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnTrueWhenIgnoreCaseIsEnabled() {
example = newExampleOf(person).matchStringsWithIgnoreCase().get();
assertThat(example.isIngnoreCaseEnabled(), is(true));
assertThat(example.isIgnoreCaseForPath("firstname"), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldFavorPathSpecificSettings() {
example = newExampleOf(person).withPropertySpecifier(ignoreCase("firstname")).get();
assertThat(example.isIngnoreCaseEnabled(), is(false));
assertThat(example.isIgnoreCaseForPath("firstname"), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void getValueTransformerForPathReturnsNoOpValueTransformerByDefault() {
assertThat(example.getValueTransformerForPath("firstname"), instanceOf(NoOpPropertyValueTransformer.class));
}
/**
* @see DATACMNS-810
*/
@Test
public void getValueTransformerForPathReturnsConfigurtedTransformerForPath() {
PropertyValueTransformer transformer = new PropertyValueTransformer() {
@Override
public Object convert(Object source) {
return source.toString();
}
};
example = newExampleOf(person).withPropertySpecifier(
newPropertySpecifier("firstname").withValueTransformer(transformer).get()).get();
assertThat(example.getValueTransformerForPath("firstname"), equalTo(transformer));
}
/**
* @see DATACMNS-810
*/
@Test
public void hasPropertySpecifiersReturnsFalseIfNoneDefined() {
assertThat(example.hasPropertySpecifiers(), is(false));
}
/**
* @see DATACMNS-810
*/
@Test(expected = UnsupportedOperationException.class)
public void getPropertiesSpecifiersShouldNotAllowAddingSpecifiers() {
example.getPropertySpecifiers().add(ignoreCase("firstname"));
}
/**
* @see DATACMNS-810
*/
@Test
public void hasPropertySpecifiersReturnsTrueWhenAtLeastOneIsSet() {
example = newExampleOf(person)
.withStringMatcher(StringMatcher.STARTING)
.withPropertySpecifier(
PropertySpecifier.newPropertySpecifier("firstname").matchString(StringMatcher.CONTAINING).get()).get();
assertThat(example.hasPropertySpecifiers(), is(true));
Example.of(null);
}
/**
@@ -250,25 +55,38 @@ public class ExampleUnitTests {
*/
@Test
public void getSampleTypeRetunsSampleObjectsClass() {
assertThat(example.getSampleType(), equalTo(Person.class));
assertThat(example.getProbeType(), equalTo(Person.class));
}
/**
* @see DATACMNS-810
*/
@Test
public void getNullHandlerShouldReturnIgnoreByDefault() {
assertThat(example.getNullHandler(), is(NullHandler.IGNORE));
public void createTypedExample() {
Example<Person> example = of(new Person(), ExampleSpec.typed(Contact.class));
assertThat(example.getProbeType(), equalTo(Person.class));
assertThat(example.getResultType(), equalTo((Class) Contact.class));
assertThat(example.getProbeType(), equalTo(Person.class));
}
/**
* @see DATACMNS-810
*/
@Test
public void getNullHandlerShouldReturnConfiguredHandler() {
public void createUntypedExample() {
example = newExampleOf(person).handleNullValues(NullHandler.INCLUDE).get();
assertThat(example.getNullHandler(), is(NullHandler.INCLUDE));
Example<Person> example = of(new Person(), ExampleSpec.untyped());
assertThat(example.getProbeType(), equalTo(Person.class));
assertThat(example.getResultType(), equalTo((Class) Person.class));
assertThat(example.getProbeType(), equalTo(Person.class));
}
static class Contact {
String label;
}
static class Person {

View File

@@ -0,0 +1,231 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.domain;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.ExampleSpec.NullHandler;
import org.springframework.data.domain.ExampleSpec.StringMatcher;
/**
* Unit test for {@link ExampleSpec}.
*
* @author Mark Paluch
* @soundtrack Harmonic Rush - The Dark Side of Persia (ahmed romel remix)
*/
public class TypedExampleSpecUnitTests {
TypedExampleSpec<Person> exampleSpec;
@Before
public void setUp() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class);
}
/**
* @see DATACMNS-810
*/
@Test
public void typeShouldReturnConfiguredType() throws Exception {
assertThat(exampleSpec.getType(), equalTo(Person.class));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnDefault() throws Exception {
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.DEFAULT));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnContainingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withStringMatcherContaining();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.CONTAINING));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnStartingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withStringMatcherStarting();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.STARTING));
}
/**
* @see DATACMNS-810
*/
@Test
public void defaultStringMatcherShouldReturnEndingWhenConfigured() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withStringMatcherEnding();
assertThat(exampleSpec.getDefaultStringMatcher(), is(StringMatcher.ENDING));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnFalseByDefault() throws Exception {
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(false));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsIsEmptyByDefault() throws Exception {
assertThat(exampleSpec.getIgnoredPaths(), is(empty()));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnIgnoreByDefault() throws Exception {
assertThat(exampleSpec.getNullHandler(), is(NullHandler.IGNORE));
}
/**
* @see DATACMNS-810
*/
@Test(expected = UnsupportedOperationException.class)
public void ignoredPathsIsNotModifiable() throws Exception {
exampleSpec.getIgnoredPaths().add("¯\\_(ツ)_/¯");
}
/**
* @see DATACMNS-810
*/
@Test(expected = IllegalArgumentException.class)
public void defaultExampleSpecWithoutTypeFails() throws Exception {
ExampleSpec.typed(null);
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnTrueWhenIgnoreCaseEnabled() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnoreCase();
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoreCaseShouldReturnTrueWhenIgnoreCaseSet() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnoreCase(true);
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(true));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnInclude() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIncludeNullValues();
assertThat(exampleSpec.getNullHandler(), is(NullHandler.INCLUDE));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnIgnore() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnoreNullValues();
assertThat(exampleSpec.getNullHandler(), is(NullHandler.IGNORE));
}
/**
* @see DATACMNS-810
*/
@Test
public void nullHandlerShouldReturnConfiguredValue() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withNullHandler(NullHandler.INCLUDE);
assertThat(exampleSpec.getNullHandler(), is(NullHandler.INCLUDE));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsShouldReturnCorrectProperties() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnorePaths("foo", "bar", "baz");
assertThat(exampleSpec.getIgnoredPaths(), contains("foo", "bar", "baz"));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(3));
}
/**
* @see DATACMNS-810
*/
@Test
public void ignoredPathsShouldReturnUniqueProperties() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnorePaths("foo", "bar", "foo");
assertThat(exampleSpec.getIgnoredPaths(), contains("foo", "bar"));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(2));
}
/**
* @see DATACMNS-810
*/
@Test
public void withCreatesNewInstance() throws Exception {
exampleSpec = ExampleSpec.typed(Person.class).withIgnorePaths("foo", "bar", "foo");
TypedExampleSpec<Person> configuredExampleSpec = exampleSpec.withIgnoreCase();
assertThat(exampleSpec, is(not(sameInstance(configuredExampleSpec))));
assertThat(exampleSpec.getIgnoredPaths(), hasSize(2));
assertThat(exampleSpec.isIgnoreCaseEnabled(), is(false));
assertThat(configuredExampleSpec.getIgnoredPaths(), hasSize(2));
assertThat(configuredExampleSpec.isIgnoreCaseEnabled(), is(true));
}
static class Person {
String firstname;
}
}