From 6337f999e91013f28d3040fe7fd55d31432d77e2 Mon Sep 17 00:00:00 2001 From: amckenzie Date: Tue, 28 Feb 2017 11:29:57 +0000 Subject: [PATCH] move utility classes from framework --- utilities-core/pom.xml | 10 + .../persistence/InitialContextFactory.java | 11 + .../services/messaging/JsonObjects.java | 299 +++++++++++++++ .../InitialContextFactoryTest.java | 23 ++ .../services/messaging/JsonObjectsTest.java | 361 ++++++++++++++++++ 5 files changed, 704 insertions(+) create mode 100644 utilities-core/src/main/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactory.java create mode 100644 utilities-core/src/main/java/uk/gov/justice/services/messaging/JsonObjects.java create mode 100644 utilities-core/src/test/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactoryTest.java create mode 100644 utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java diff --git a/utilities-core/pom.xml b/utilities-core/pom.xml index 4b511c5..d729b7b 100644 --- a/utilities-core/pom.xml +++ b/utilities-core/pom.xml @@ -42,6 +42,11 @@ com.fasterxml.jackson.core jackson-databind + + com.google.guava + guava + + @@ -59,6 +64,11 @@ mockito-core test + + org.hamcrest + hamcrest-library + test + org.skyscreamer jsonassert diff --git a/utilities-core/src/main/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactory.java b/utilities-core/src/main/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactory.java new file mode 100644 index 0000000..40252d8 --- /dev/null +++ b/utilities-core/src/main/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactory.java @@ -0,0 +1,11 @@ +package uk.gov.justice.services.jdbc.persistence; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +public class InitialContextFactory { + + public InitialContext create() throws NamingException { + return new InitialContext(); + } +} diff --git a/utilities-core/src/main/java/uk/gov/justice/services/messaging/JsonObjects.java b/utilities-core/src/main/java/uk/gov/justice/services/messaging/JsonObjects.java new file mode 100644 index 0000000..f5427b8 --- /dev/null +++ b/utilities-core/src/main/java/uk/gov/justice/services/messaging/JsonObjects.java @@ -0,0 +1,299 @@ +package uk.gov.justice.services.messaging; + +import static javax.json.JsonValue.ValueType; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonString; +import javax.json.JsonValue; + +import com.google.common.collect.ImmutableList; + +/** + * Collection of static utility methods for getting deep values from a {@link JsonObject}. + */ +public final class JsonObjects { + + private static final String FIELD_IS_NOT_A_TYPE = "Field %s is not a %s"; + + /** + * Private constructor to prevent misuse of utility class. + */ + private JsonObjects() { + } + + /** + * Returns the (possibly nested) array value to which the specified name is mapped, if it + * exists. + * + * @param object the JsonObject from which to retrieve the value + * @param names the field name path whose associated value is to be returned + * @return the array value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonArray type + */ + public static Optional getJsonArray(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.ARRAY, JsonObject::getJsonArray, names); + } + + /** + * Returns the (possibly nested) object value to which the specified name is mapped, if it + * exists. + * + * @param object the JsonObject from which to retrieve the value + * @param names the field name path whose associated value is to be returned + * @return the object value to which the specified name is mapped, or {@code null} if this + * object contains no mapping for the name + * @throws IllegalStateException if the value is not assignable to JsonObject type + */ + public static Optional getJsonObject(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.OBJECT, JsonObject::getJsonObject, names); + } + + /** + * Returns the (possibly nested) number value to which the specified name is mapped, if it + * exists. + * + * @param object the JsonObject from which to retrieve the value + * @param names the field name path whose associated value is to be returned + * @return the number value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonNumber type + */ + public static Optional getJsonNumber(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.NUMBER, JsonObject::getJsonNumber, names); + } + + /** + * Returns the (possibly nested) string value to which the specified name is mapped, if it + * exists. + * + * @param object the JsonObject from which to retrieve the value + * @param names the field name path whose associated value is to be returned + * @return the string value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonString type + */ + public static Optional getJsonString(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.STRING, JsonObject::getJsonString, names); + } + + /** + * Returns a (possibly nested) value converted into a specified JsonValue type. + * + * @param object the JsonObject from which to retrieve the value + * @param valueType the type of JsonValue we need to return + * @param function the function to use to get the correct type of JsonValue from the + * JsonObject + * @param names the field name path whose associated value is to be returned + * @param the type of JsonValue that will be returned + * @return an optional value found at the specified location in the JsonObject + */ + private static Optional getJsonValue(final JsonObject object, + final ValueType valueType, + final BiFunction function, + final String... names) { + checkArguments(object, names); + if (names.length == 1) { + if (!object.containsKey(names[0])) { + return Optional.empty(); + } + ValueType actualValueType = object.get(names[0]).getValueType(); + + if (ValueType.NULL.equals(actualValueType)) { + return Optional.empty(); + } + + if (!valueType.equals(actualValueType)) { + throw new IllegalStateException(String.format(FIELD_IS_NOT_A_TYPE, names[0], valueType.toString())); + } + + return Optional.of(function.apply(object, names[0])); + } else { + return getJsonObject(object, names[0]) + .flatMap(subObject -> getJsonValue(subObject, valueType, function, Arrays.copyOfRange(names, 1, names.length))); + } + } + + /** + * A convenience method for {@code getJsonString(name).get().getString()}. + * + * @param object the JsonObject from which to retrieve the value + * @param names whose associated value is to be returned as String + * @return the String value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonString type + */ + public static Optional getString(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.STRING, JsonObject::getJsonString, names) + .map(JsonString::getString); + } + + /** + * A convenience method for {@code UUID.fromString(getJsonString(name).get().getString())}. + * + * @param object the JsonObject from which to retrieve the value + * @param names whose associated value is to be returned as String + * @return the String value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonString type + * @throws IllegalArgumentException if the value is not assignable to a UUID + */ + public static Optional getUUID(final JsonObject object, final String... names) { + return getString(object, names) + .map(string -> { + try { + return UUID.fromString(string); + } catch (IllegalArgumentException ex) { + throw new IllegalStateException(String.format("Retrieved string '%s' is not a UUID", string), ex); + } + }); + } + + /** + * A convenience method for {@code JsonNumber.longValue}. + * + * @param object the JsonObject from which to retrieve the value + * @param names whose associated value is to be returned as Long + * @return the Long value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to JsonNumber type + * @throws IllegalArgumentException if the value is not assignable to a Long + */ + public static Optional getLong(final JsonObject object, final String... names) { + return getJsonValue(object, ValueType.NUMBER, JsonObject::getJsonNumber, names) + .map(JsonNumber::longValue); + } + + /** + * A convenience method to retrieve a Boolean value + * + * @param object the JsonObject from which to retrieve the value + * @param name whose associated value is to be returned as Long + * @return the Boolean value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to a Boolean + */ + public static Optional getBoolean(final JsonObject object, final String name) { + try { + return object.containsKey(name) ? Optional.of(object.getBoolean(name)) : Optional.empty(); + } catch (ClassCastException e) { + throw new IllegalStateException(String.format(FIELD_IS_NOT_A_TYPE, name, "Boolean")); + } + } + + + + /** + * A convenience method for getting a JsonArray as a List of a specific JsonValue type. + * + * @param object the JsonObject from which to retrieve the value + * @param names whose associated value is to be returned + * @param clazz the type of JsonValue that the returned list will contain + * @return the Long value to which the specified name is mapped + * @throws IllegalStateException if the value is not assignable to the specified type + * @throws IllegalArgumentException if the value is not assignable to a Long + */ + public static Optional> getList(final JsonObject object, final Class clazz, final String... names) { + return getJsonValue(object, ValueType.ARRAY, JsonObject::getJsonArray, names) + .map(jsonArray -> jsonArray.getValuesAs(clazz)) + .map(ImmutableList::copyOf); + } + + /** + * A convenience method for getting a list of a specific type. + * + * @param object the JsonObject from which to retrieve the value + * @param jsonClazz the JsonValue type that the value is stored as + * @param converter a function that can convert from the JsonValue class to the required type + * @param names whose associated value is to be returned + * @param the type of items in the return list + * @param the JsonValue type that the value is stored as + * @return an optional list of values found + */ + public static Optional> getList(final JsonObject object, + final Class jsonClazz, + final Function converter, + final String... names) { + return getList(object, jsonClazz, names) + .map(list -> list.stream() + .map(converter) + .collect(Collectors.toList())) + .map(ImmutableList::copyOf); + } + + /** + * Get a list of UUIDs from a JsonObject. + * + * @param object object the JsonObject from which to retrieve the list + * @param names the field name path whose associated value is to be returned + * @return the list of UUIDs or an empty list if none were found + */ + public static List getUUIDs(final JsonObject object, final String... names) { + return getList(object, JsonString.class, jsonString -> UUID.fromString(jsonString.getString()), names) + .orElse(Collections.emptyList()); + } + + /** + * Create a {@link JsonObjectBuilder} from an existing {@link JsonObject} applying the filter. + * Only copy the field names for which the filter returns true. + * + * @param source {@link JsonObject} to copy fields from + * @return a {@link JsonObjectBuilder} initialised with the fields contained in the source + */ + public static JsonObjectBuilder createObjectBuilderWithFilter(final JsonObject source, Function filter) { + JsonObjectBuilder builder = Json.createObjectBuilder(); + source.entrySet().stream().filter(e -> filter.apply(e.getKey())).forEach(x -> builder.add(x.getKey(), x.getValue())); + return builder; + } + + /** + * Create a {@link JsonObjectBuilder} from an existing {@link JsonObject}. + * + * @param source {@link JsonObject} to copy fields from + * @return a {@link JsonObjectBuilder} initialised with the fields contained in the source + */ + public static JsonObjectBuilder createObjectBuilder(final JsonObject source) { + return createObjectBuilderWithFilter(source, x -> true); + } + + /** + * Assert that the provided arguments are valid. The object must not be null, and the next field + * name must be non-empty. + * + * @param object the JsonObject from which to retrieve the value + * @param names the field names + */ + private static void checkArguments(final JsonObject object, final String... names) { + if (object == null) { + throw new IllegalArgumentException("Json object cannot be null"); + } + if (names.length == 0) { + throw new IllegalArgumentException("At least one level of field name must be provided"); + } + if (names[0] == null || names[0].isEmpty()) { + throw new IllegalArgumentException("Field name cannot be null or empty"); + } + } + + /** + * Convert a collection of objects into a JsonArray + * + * @param entries the collection to be converted + * @param converter to convert each entry in the collection + * @param the type of element in the collection + * @return a JsonArray with the converted entries + */ + public static JsonArray toJsonArray(final Collection entries, final Function converter) { + final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + entries.forEach(entry -> arrayBuilder.add(converter.apply(entry))); + return arrayBuilder.build(); + } +} diff --git a/utilities-core/src/test/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactoryTest.java b/utilities-core/src/test/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactoryTest.java new file mode 100644 index 0000000..7211a55 --- /dev/null +++ b/utilities-core/src/test/java/uk/gov/justice/services/jdbc/persistence/InitialContextFactoryTest.java @@ -0,0 +1,23 @@ +package uk.gov.justice.services.jdbc.persistence; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import javax.naming.InitialContext; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class InitialContextFactoryTest { + + private InitialContextFactory initialContextFactory = new InitialContextFactory(); + + @Test + public void shouldCreateANewInitialContext() throws Exception { + + assertThat(initialContextFactory.create(), is(instanceOf(InitialContext.class))); + } +} diff --git a/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java b/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java new file mode 100644 index 0000000..869a788 --- /dev/null +++ b/utilities-core/src/test/java/uk/gov/justice/services/messaging/JsonObjectsTest.java @@ -0,0 +1,361 @@ +package uk.gov.justice.services.messaging; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static javax.json.Json.createArrayBuilder; +import static javax.json.Json.createObjectBuilder; +import static javax.json.JsonValue.NULL; +import static net.trajano.commons.testing.UtilityClassTestUtil.assertUtilityClassWellDefined; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; + +import javax.json.JsonArray; +import javax.json.JsonNumber; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonString; +import javax.json.JsonValue; + +import com.google.common.collect.ImmutableList; +import org.junit.Test; + +/** + * Unit tests for the {@link JsonObjects} class. + */ +public class JsonObjectsTest { + + private static final String UUID_A = "da45e8f6-d945-4f09-a115-1139a9dbb754"; + private static final String UUID_B = "d04885b4-9652-4c2a-87c6-299bda0a87d4"; + + @Test + public void shouldBeWellDefinedUtilityClass() { + assertUtilityClassWellDefined(JsonObjects.class); + } + + @Test + public void shouldReturnJsonArray() { + JsonArray array = createArrayBuilder() + .addNull() + .build(); + JsonObject object = createObjectBuilder() + .add("name", array) + .build(); + Optional jsonArray = JsonObjects.getJsonArray(object, "name"); + + assertThat(jsonArray.isPresent(), is(true)); + assertThat(jsonArray.get(), equalTo(array)); + } + + @Test + public void shouldReturnJsonObject() { + JsonObject subObject = createObjectBuilder() + .add("name2", "cheese") + .build(); + JsonObject object = createObjectBuilder() + .add("name", subObject) + .build(); + Optional jsonObject = JsonObjects.getJsonObject(object, "name"); + + assertThat(jsonObject.isPresent(), is(true)); + assertThat(jsonObject.get(), equalTo(subObject)); + } + + @Test + public void shouldReturnJsonNumber() { + JsonObject object = createObjectBuilder() + .add("name", 99L) + .build(); + Optional jsonNumber = JsonObjects.getJsonNumber(object, "name"); + + assertThat(jsonNumber.isPresent(), is(true)); + assertThat(jsonNumber.get().longValue(), equalTo(99L)); + } + + @Test + public void shouldReturnJsonString() { + JsonObject object = createObjectBuilder() + .add("name", "test") + .build(); + Optional jsonString = JsonObjects.getJsonString(object, "name"); + + assertThat(jsonString.isPresent(), is(true)); + assertThat(jsonString.get().getString(), equalTo("test")); + } + + @Test + public void shouldReturnJsonStringForNestedField() { + JsonObject object = createObjectBuilder() + .add("name", createObjectBuilder() + .add("name2", "test") + .build()) + .build(); + Optional jsonString = JsonObjects.getJsonString(object, "name", "name2"); + + assertThat(jsonString.isPresent(), is(true)); + assertThat(jsonString.get().getString(), equalTo("test")); + } + + @Test + public void shouldReturnString() { + JsonObject object = createObjectBuilder() + .add("name", "test") + .build(); + Optional string = JsonObjects.getString(object, "name"); + + assertThat(string.isPresent(), is(true)); + assertThat(string.get(), equalTo("test")); + } + + @Test + public void shouldReturnBoolean() { + JsonObject object = createObjectBuilder() + .add("someBoolean", true) + .build(); + Optional someBoolean = JsonObjects.getBoolean(object, "someBoolean"); + + assertThat(someBoolean.isPresent(), is(true)); + assertThat(someBoolean.get(), is(true)); + } + + + @Test + public void shouldReturnEmptyIfBooleanFieldUnknown() { + JsonObject object = createObjectBuilder() + .build(); + Optional someBoolean = JsonObjects.getBoolean(object, "someBoolean"); + + assertThat(someBoolean.isPresent(), is(false)); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionForNonBoolean() { + JsonObject object = createObjectBuilder() + .add("someBool", 99L) + .build(); + JsonObjects.getBoolean(object, "someBool"); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionForNonString() { + JsonObject object = createObjectBuilder() + .add("name", 99L) + .build(); + JsonObjects.getString(object, "name"); + } + + @Test + public void shouldReturnUUID() { + final String stringValue = "6c84963d-47a1-4d57-a706-09bea3fa84a5"; + JsonObject object = createObjectBuilder() + .add("name", stringValue) + .build(); + Optional uuid = JsonObjects.getUUID(object, "name"); + + assertThat(uuid.isPresent(), is(true)); + assertThat(uuid.get(), equalTo(UUID.fromString(stringValue))); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionForNonUUID() { + JsonObject object = createObjectBuilder() + .add("name", "blah") + .build(); + JsonObjects.getUUID(object, "name"); + } + + @Test + public void shouldReturnLong() { + JsonObject object = createObjectBuilder() + .add("name", 99L) + .build(); + Optional string = JsonObjects.getLong(object, "name"); + + assertThat(string.isPresent(), is(true)); + assertThat(string.get(), equalTo(99L)); + } + + @Test(expected = IllegalStateException.class) + public void shouldThrowExceptionForNonLong() { + JsonObject object = createObjectBuilder() + .add("name", "blah") + .build(); + JsonObjects.getLong(object, "name"); + } + + @Test + public void shouldReturnListOfJsonStrings() { + JsonArray array = createArrayBuilder() + .add("test1") + .add("test2") + .build(); + JsonObject object = createObjectBuilder() + .add("name", array) + .build(); + Optional> jsonStrings = JsonObjects.getList(object, JsonString.class, "name"); + + assertThat(jsonStrings.isPresent(), is(true)); + assertThat(jsonStrings.get(), hasSize(2)); + assertThat(jsonStrings.get().get(0).getString(), equalTo("test1")); + assertThat(jsonStrings.get().get(1).getString(), equalTo("test2")); + } + + @Test + public void shouldReturnListOfStrings() { + JsonArray array = createArrayBuilder() + .add("test1") + .add("test2") + .build(); + JsonObject object = createObjectBuilder() + .add("name", array) + .build(); + Optional> strings = JsonObjects.getList(object, JsonString.class, JsonString::getString, "name"); + + assertThat(strings.isPresent(), is(true)); + assertThat(strings.get(), equalTo(ImmutableList.of("test1", "test2"))); + } + + @Test + public void shouldReturnListOfUUIDs() { + JsonArray array = createArrayBuilder() + .add(UUID_A) + .add(UUID_B) + .build(); + JsonObject object = createObjectBuilder() + .add("name", array) + .build(); + List uuids = JsonObjects.getUUIDs(object, "name"); + + assertThat(uuids, equalTo(ImmutableList.of(UUID.fromString(UUID_A), UUID.fromString(UUID_B)))); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForNullObject() { + JsonObjects.getString(null, "name"); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForNullFieldName() { + JsonObject object = createObjectBuilder() + .add("name", 99L) + .build(); + JsonObjects.getString(object, (String) null); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForEmptyVarArgs() { + JsonObject object = createObjectBuilder() + .add("name", 99L) + .build(); + JsonObjects.getString(object); + } + + @Test + public void shouldReturnEmptyIfFieldIsUnknown() { + JsonObject object = createObjectBuilder() + .add("name", "test") + .build(); + Optional string = JsonObjects.getString(object, "name2"); + + assertThat(string.isPresent(), is(false)); + } + + @Test + public void shouldReturnEmptyIfFieldValueIsNull() { + JsonObject object = createObjectBuilder() + .add("name", NULL) + .build(); + Optional string = JsonObjects.getString(object, "name"); + + assertThat(string.isPresent(), is(false)); + } + + @Test + public void shouldCreateBuilderFromJsonObject() { + JsonObject source = createObjectBuilder() + .add("name", "test") + .build(); + + JsonObjectBuilder builder = JsonObjects.createObjectBuilder(source); + + assertThat(builder.build(), equalTo(source)); + } + + @Test + public void shouldCreateBuilderFromJsonObjectWithFilter() { + JsonObject source = createObjectBuilder() + .add("id", "test id") + .add("ignore1", "ignore this") + .add("name", "test") + .add("ignore2", "ignore this as well") + .build(); + + JsonObjectBuilder builder = JsonObjects.createObjectBuilderWithFilter(source, x -> !"ignore1".equals(x) && !"ignore2".equals(x)); + + JsonObject actual = builder.build(); + assertThat(actual.size(), equalTo(2)); + assertThat(actual.getString("id"), equalTo(source.getString("id"))); + assertThat(actual.getString("name"), equalTo(source.getString("name"))); + } + + @Test + public void shouldConvertCollectionOfJsonObjectsToJsonArray() { + + final String oldKey = "oldKey"; + final String newKey = "newKey"; + + final JsonArray input = createArrayBuilder() + .add(createObjectBuilder().add(oldKey, "value1")) + .add(createObjectBuilder().add(oldKey, "value2")) + .add(createObjectBuilder().add(oldKey, "value3")) + .build(); + + final Function converter = source -> createObjectBuilder() + .add(newKey, source.getString(oldKey)) + .build(); + + final JsonArray actual = JsonObjects.toJsonArray(input.getValuesAs(JsonObject.class), converter); + + final JsonArray expected = createArrayBuilder() + .add(createObjectBuilder().add(newKey, "value1")) + .add(createObjectBuilder().add(newKey, "value2")) + .add(createObjectBuilder().add(newKey, "value3")) + .build(); + + assertThat(actual, equalTo(expected)); + } + + @Test + public void shouldConvertCollectionOfObjectsToJsonArray() { + final String key = "key"; + final String value = "TEST"; + + final Function converter = source -> createObjectBuilder().add(key, source).build(); + + final JsonArray result = JsonObjects.toJsonArray(asList(value), converter); + + final JsonArray expected = createArrayBuilder() + .add(createObjectBuilder().add(key, value)) + .build(); + + assertThat(result, equalTo(expected)); + + } + + @Test + public void shouldConvertEmptyCollectionToJsonArray() { + + final JsonArray result = JsonObjects.toJsonArray(emptyList(), source -> createObjectBuilder().build()); + + final JsonArray expected = createArrayBuilder().build(); + + assertThat(result, equalTo(expected)); + } + +}