Question
Assume the following simple test:
#Test
public void test() throws Exception {
Object value = 1;
assertThat(value, greaterThan(0));
}
The test won't compile, because "greaterThan" can only be applied to instances of type Comparable. But I want to assert that value is an Integer which is greater than zero. How can I express that using Hamcrest?
What I tried so far:
The simple solution would be to simply remove the generics by casting the matcher like that:
assertThat(value, (Matcher)greaterThan(0));
Possible, but generates a compiler warning and feels wrong.
A lengthy alternative is:
#Test
public void testName() throws Exception {
Object value = 1;
assertThat(value, instanceOfAnd(Integer.class, greaterThan(0)));
}
private static<T> Matcher<Object> instanceOfAnd(final Class<T> clazz, final Matcher<? extends T> submatcher) {
return new BaseMatcher<Object>() {
#Override
public boolean matches(final Object item) {
return clazz.isInstance(item) && submatcher.matches(clazz.cast(item));
}
#Override
public void describeTo(final Description description) {
description
.appendText("is instanceof ")
.appendValue(clazz)
.appendText(" and ")
.appendDescriptionOf(submatcher);
}
#Override
public void describeMismatch(final Object item, final Description description) {
if (clazz.isInstance(item)) {
submatcher.describeMismatch(item, description);
} else {
description
.appendText("instanceof ")
.appendValue(item == null ? null : item.getClass());
}
}
};
}
Feels "tidy" and "correct", but it is really a lot of code for something that seems simple. I attempted to find something like that built-in in hamcrest, but I was not successful, but maybe I missed something?
Background
In my actual test case the code is like this:
Map<String, Object> map = executeMethodUnderTest();
assertThat(map, hasEntry(equalTo("the number"), greaterThan(0)));
In my simplified case in the question I could also write assertThat((Integer)value, greaterThan(0)). In my actual case I could write assertThat((Integer)map.get("the number"), greaterThan(0)));, but that would of course worsen the error message if something is wrong.
This answer will not show how to do this using Hamcrest, I do not know if there is a better way than the proposed.
However, if you have the possibility to include another test library, AssertJ supports exactly this:
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestClass {
#Test
public void test() throws Exception {
Object value = 1;
assertThat(value).isInstanceOfSatisfying(Integer.class, integer -> assertThat(integer).isGreaterThan(0));
}
}
No need for any casting, AssertJ does this for you.
Also, it prints a pretty error message if the assertion fails, with value being too small:
java.lang.AssertionError:
Expecting:
<0>
to be greater than:
<0>
Or if value is not of the correct type:
java.lang.AssertionError:
Expecting:
<"not an integer">
to be an instance of:
<java.lang.Integer>
but was instance of:
<java.lang.String>
The Javadoc for isInstanceOfSatisfying(Class<T> type, Consumer<T> requirements) can be found here, which also contains some examples of sligthly more complicated assertions:
// second constructor parameter is the light saber color
Object yoda = new Jedi("Yoda", "Green");
Object luke = new Jedi("Luke Skywalker", "Green");
Consumer<Jedi> jediRequirements = jedi -> {
assertThat(jedi.getLightSaberColor()).isEqualTo("Green");
assertThat(jedi.getName()).doesNotContain("Dark");
};
// assertions succeed:
assertThat(yoda).isInstanceOfSatisfying(Jedi.class, jediRequirements);
assertThat(luke).isInstanceOfSatisfying(Jedi.class, jediRequirements);
// assertions fail:
Jedi vader = new Jedi("Vader", "Red");
assertThat(vader).isInstanceOfSatisfying(Jedi.class, jediRequirements);
// not a Jedi !
assertThat("foo").isInstanceOfSatisfying(Jedi.class, jediRequirements);
The problem is that you lose the type information here:
Object value = 1;
This is an insanely weird line, if you think about it. Here value is the most generic thing possible, nothing can be reasonably told about it, except maybe checking if it's null or checking its string representation if it's not. I'm sort of at loss trying to imagine a legitimate use case for the above line in modern Java.
The obvious fix would be assertThat((Comparable)value, greaterThan(0));
A better fix would be casting to Integer, because you're comparing to an integer constant; strings are also comparable but only between themselves.
If you can't assume that your value is even Comparable, comparing it to anything is pointless. If your test fails on the cast to Comparable, it's a meaningful report that you dynamic casting to Object from something else failed.
How about a slighly modified version of your original attempt:
#Test
public void testName() throws Exception {
Map<String, Object> map = executeMethodUnderTest();
assertThat(map, hasEntry(equalTo("the number"),
allOf(instanceOf(Integer.class), integerValue(greaterThan(0)))));
}
private static<T> Matcher<Object> integerValue(final Matcher<T> subMatcher) {
return new BaseMatcher<Object>() {
#Override
public boolean matches(Object item) {
return subMatcher.matches(Integer.class.cast(item));
}
#Override
public void describeTo(Description description) {
description.appendDescriptionOf(subMatcher);
}
#Override
public void describeMismatch(Object item, Description description) {
subMatcher.describeMismatch(item, description);
}
};
}
Now the custom matcher is a little less verbose and you still achieve what you want.
If the value is too small:
java.lang.AssertionError:
Expected: map containing ["the number"->(an instance of java.lang.Integer and a value greater than <0>)]
but: map was [<the number=0>]
If the value is wrong type:
java.lang.AssertionError:
Expected: map containing ["the number"->(an instance of java.lang.Integer and a value greater than <0>)]
but: map was [<the number=something>]
The problem with map containing Object values is that you have to assume the specific class to compare.
What hamcrest is lacking is a way to transform a matcher from a given type to another, such as the one in this gist:
https://gist.github.com/dmcg/8ddf275688fd450e977e
public class TransformingMatcher<U, T> extends TypeSafeMatcher<U> {
private final Matcher<T> base;
private final Function<? super U, ? extends T> function;
public TransformingMatcher(Matcher<T> base, Function<? super U, ? extends T> function) {
this.base = base;
this.function = function;
}
#Override
public void describeTo(Description description) {
description.appendText("transformed version of ");
base.describeTo(description);
}
#Override
protected boolean matchesSafely(U item) {
return base.matches(function.apply(item));
}
}
With that, you could write your asserts this way:
#Test
public void testSomething() {
Map<String, Object> map = new HashMap<>();
map.put("greater", 5);
assertThat(map, hasEntry(equalTo("greater"), allOf(instanceOf(Number.class),
new TransformingMatcher<>(greaterThan((Comparable)0), new Function<Object, Comparable>(){
#Override
public Comparable apply(Object input) {
return Integer.valueOf(input.toString());
}
}))));
}
But the problem, again, is that you need to specify a given Comparable numeric class (Integer in this case).
In case of assertion error the message would be:
java.lang.AssertionError
Expected: map containing ["string"->(an instance of java.lang.Number and transformed version of a value greater than <0>)]
but: map was [<string=NaN>]
Related
I have the following classes
public class MyCustomFactory extends SomeOther3rdPartyFactory {
// Return our custom behaviour for the 'string' type
#Override
public StringType stringType() {
return new MyCustomStringType();
}
// Return our custom behaviour for the 'int' type
#Override
public IntType intType() {
return new MyCustomIntType();
}
// same for boolean, array, object etc
}
Now, for example, the custom type classes:
public class MyCustomStringType extends StringType {
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
if (prop.getSomeAttribute("attribute01")) {
this.doSomething();
this.doSomethingElse();
}
if (prop.getSomeAttribute("attribute02")) {
this.doSomethingYetAgain();
}
// other properties and actions
}
}
But each custom type class like the string one above might have exactly the same if (prop.getSomeAttribute("blah")) { // same thing; }
Suppose I was to add another attribute, is there a nice way I can avoid having to duplicate if statements in each custom type class that needs it? I can move each if statement to utility class but I still need to add the call to the method in the utility class. I think we can do better.
You can create Map<String, Consumer<MyCustomStringType>>, where the key is your attribute name and value is the method call.
public class MyCustomStringType extends StringType {
private final Map<String, Cosnumer<MyCustomStringType>> map = new HashMap<>();
{
map.put("attribute01", o -> {o.doSomething(); o.doSomethingElse();});
map.put("attribute02", MyCustomStringType::doSomethingYetAgain);
// other properties and actions
}
#Override
public void enrichWithProperty(final SomePropertyObject prop) {
super.enrichWithProperty(prop);
map.entrySet().stream()
.filter(entry -> prop.getSomeAttribute(entry.getKey()))
.forEach(entry -> entry.getValue().accept(MyCustomStringType.this));
}
}
Depending on how you initialise this class (and whether this map is always the same), you might be able to turn in into static final immutable map.
I would also recommend naming it better, but a lot here depends on your domain and what this map and loop actually do.
I'm running into a generics problem with Mockito and Hamcrest.
Please assume the following interface:
public interface Service {
void perform(Collection<String> elements);
}
And the following test snippet:
Service service = mock(Service.class);
// ... perform business logic
verify(service).perform(Matchers.argThat(contains("a", "b")));
So I want to verify that my business logic actually called the service with a collection that contains "a" and "b" in that order.
However, the return type of contains(...) is Matcher<Iterable<? extends E>>, so Matchers.argThat(...) returns Iterable<String> in my case, which naturally does not apply to the required Collection<String>.
I know that I could use an argument captor as proposed in Hamcrest hasItem and Mockito verify inconsistency, but I would very much like not to.
Any suggestions!
Thanks!
You can just write
verify(service).perform((Collection<String>) Matchers.argThat(contains("a", "b")));
From the compiler's point of view, this is casting an Iterable<String> to a Collection<String> which is fine, because the latter is a subtype of the former. At run time, argThat will return null, so that can be passed to perform without a ClassCastException. The important point about it is that the matcher gets onto Mockito's internal structure of arguments for verification, which is what argThat does.
As an alternative one could change the approach to ArgumentCaptor:
#SuppressWarnings("unchecked") // needed because of `List<String>.class` is not a thing
// suppression can be worked around by using #Captor on a field
ArgumentCaptor<List<String>> captor = ArgumentCaptor.forClass(List.class);
verify(service).perform(captor.capture());
assertThat(captor.getValue(), contains("a", "b"));
Notice, that as a side effect this decouples the verification from the Hamcrest library, and allows you to use any other library (e.g. Truth):
assertThat(captor.getValue()).containsExactly("a", "b");
If you get stuck in situations like these, remember that you can write a very small reusable adapter.
verify(service).perform(argThat(isACollectionThat(contains("foo", "bar"))));
private static <T> Matcher<Collection<T>> isACollectionThat(
final Matcher<Iterable<? extends T>> matcher) {
return new BaseMatcher<Collection<T>>() {
#Override public boolean matches(Object item) {
return matcher.matches(item);
}
#Override public void describeTo(Description description) {
matcher.describeTo(description);
}
};
}
Note that David's solution above, with casting, is the shortest right answer.
You can put your own lambda as an ArgumentMatcher
when(myClass.myMethod(argThat(arg -> arg.containsAll(asList(1,2))))
.thenReturn(...);
Why not just verify with the expected arguments, assuming the list only contains the two items, e.g.:
final List<String> expected = Lists.newArrayList("a", "b");
verify(service).perform(expected);
Whilst I agree with Eugen in principle, I think that relying on equals for String comparison is acceptable... besides, the contains matcher uses equals for comparison anyway.
Similar to another answer here you can do the following:
verify(yourmock, times(1)).yourmethod(argThat(arg -> arg.containsAll(asList("a", "b"))));
You could have your own java.util.Collection implementation and override the equals method like below.
public interface Service {
void perform(Collection<String> elements);
}
#Test
public void testName() throws Exception {
Service service = mock(Service.class);
service.perform(new HashSet<String>(Arrays.asList("a","b")));
Mockito.verify(service).perform(Matchers.eq(new CollectionVerifier<String>(Arrays.asList("a","b"))));
}
public class CollectionVerifier<E> extends ArrayList<E> {
public CollectionVerifier() {
}
public CollectionVerifier(final Collection<? extends E> c) {
super(c);
}
#Override
public boolean equals(final Object o) {
if (o instanceof Collection<?>) {
Collection<?> other = (Collection<?>) o;
return this.size() == other.size() && this.containsAll(other);
}
return false;
}
}
I want to have a Map object to contain specific value types.
Map<String,Object> foo = new HashMap<String,Object>();
foo.put("1",new Integer(1));
foo.put("2", new String("hello"):
for (Map.Entry<Integer, Integer> entry : foo.entrySet()) {
if(entry.getValue() instanceof String) {
//do something
}
else if(entry.getValue() instanceof Double) {
//throw Exception
}
}
You can see what i am trying to accomplish. I need specific Value types in my Map object so i don't have to put lots of if/else statements. How do i accomplish this ?
If you don't like the idea of having multiple maps, you can use the nice OOP feature called overriding.
see Override Methods for more information.
I would probably use composition pattern to wrap the value types and then define a interface for the action depending of the value types.
public abstract class ValueType {
public abstract void valueSpecificAction();
}
public class DoubleValueType extends ValueType {
private Double value;
public DoubleValueType(Double value) {
this.value = value;
}
public void valueSpecificAction() {
//do double specific actions
}
}
public class StringValueType extends ValueType {
private String value;
public void valueSpecificAction() {
//do string specific actions
}
}
You can then add the objects of type ValueType in the maps.
Map<String,ValueType> foo = new HashMap<String,ValueType>();
foo.put("key1", new DoubleValueType(42.0));
later you can get the value, which has the type ValueType with the public method valueSpecificAction() which do the stuff depending on the specific type.
I don't see how you can avoid to have an if-else somewhere in general.
Jan's solution would spare you the if-else only if the value types can be determine at compulation-time. That is the case in the toy example above but is it so in your actual problem?
If it is a matter of making the code look cleaner you can always hide the if-else and conversions in a set of private methods.
I have one supertype defined as:
public abstract class AType<T> {
....
private T value;
private T mask;
public T getValue() {
if (isMasking())
return null;
return this.value;
}
public void setValue(T value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = value;
}
protected T getMask() {
if (!isMasking())
return null;
return this.mask;
}
protected void setMask(T mask) {
if (mask == null)
throw new IllegalArgumentException("Mask is mandatory.");
this.setMasking(true);
this.mask = mask;
}
...
}
and few subtypes like:
public class SpecType extends AType<Integer> {
...
}
these sub types specifies the unknown parameter.... i have more f.e. IPv4, Long, and so on
now i need to somehow in runtime do a dynamic cast...
i have these classes defined in enum like this:
public enum Type {
SOME_TYPE(new TypeID(0, (short) 0), OFMU16.class,
new Instantiable<AType<?>>() {
#Override
public SpecType instantiate() {
return new SpecType(new OFMatchTypeIdentifier(0, (short) 0));
}
}),...;
...
public Class<? extends AType<?>> toClass() {
return this.clazz;
}
...
}
I want do something like:
AType<?> type = SOME_TYPE.newInstance(); //this works
SOME_TYPE.toClass().cast(type).setValue(10); //this don't work
so I have to do it statically:
((SpecType) type).setValue(10);
Everything would be OK, but the user of this module will not want to look in enum and cast manually every time. This will probably make mistakes and spend a lot of time with debugging :/....
My question is how can I refactor this or how do I define structure of inheritance to allow user to cast dynamically? Is it possible?
Edit:
I am parsing packets from network. There is a lot types which differs in Vendor Type identifier and type of Value/Mask - these fields are all constant for every this combination, so i has defined it as enum constants. F.e. 20 have different only TypeID but same VendorID and all of them can be represented as Integer, next 10 differ in VendorID And TypeID but all of them can be represented as Short and so on.
It's still not clear why you should have to cast at all. As soon as SOME_TYPE is written into your sourcecode OR the type of set setValue method is hardcoded (in your example int or Integer) you don't need runtime checking - you need compile time checking.
So I suppose the following snippet is how your API users should code:
public class TypeTest {
public static void main(String[] args) {
AType<Integer> type0 = Types.SOME_TYPE_0.instantiate();
type0.setValue(10);
AType<String> type1 = Types.SOME_TYPE_1.instantiate();
type1.setValue("foo");
}
}
I have stripped down your example to the bare minimum which is required to understand the Generics part:
abstract class AType<T> {
private T value;
// standard getter/setter
public T getValue() { return this.value; }
public void setValue(T value) { this.value = value; }
}
class SpecTypeInt extends AType<Integer> {
}
class SpecTypeString extends AType<String> {
}
interface Instantiable<T> {
T instantiate();
}
The key part is: Don't use an enum, because an enum cannot have type parameters. You can use a plain interface instead like the next snippet. Each reference in the interface points to a factory. Each factory knows a) the abstract type and b) the concrete type. To make Generics happy you have to glue a) and b) together with ? extends X.
interface Types {
Instantiable<? extends AType<Integer>> SOME_TYPE_0 = new Instantiable<SpecTypeInt>() {
#Override
public SpecTypeInt instantiate() {
return new SpecTypeInt();
}
};
Instantiable<? extends AType<String>> SOME_TYPE_1 = new Instantiable<SpecTypeString>() {
#Override
public SpecTypeString instantiate() {
return new SpecTypeString();
}
} ;
}
Cleanup: Must your user look into the interface: Yes, he must in any case, because he must know which is the appropriate type for setValue 1. NO solution can circumvent this. Although Eclipse might help you and your users a little bit: In main just type Types.SOME_TYPE_1.instantiate(); then go to the start of the line, hit Ctrl2 + L ("Assign to loccal variable") and Eclipse replaces the AType<String> instantiate = part for you.
1If your users don't know the right type for the setValue method, then you are asking the wrong question. In that case you should have asked something like "How to design a Generic safe conversion facility?".
Maybe using a setValue method like this:
public void setValue(Object value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = (T)value;
}
Although you will have an unchecked cast.
Hope this helps
Why is the compiler unable to infer the correct type for the result from Collections.emptySet() in the following example?
import java.util.*;
import java.io.*;
public class Test {
public interface Option<A> {
public <B> B option(B b, F<A,B> f);
}
public interface F<A,B> {
public B f(A a);
}
public Collection<String> getColl() {
Option<Integer> iopt = null;
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
}
Here's the compiler error message:
knuttycombe#knuttycombe-ubuntu:~/tmp/java$ javac Test.java
Test.java:16: <B>option(B,Test.F<java.lang.Integer,B>) in
Test.Option<java.lang.Integer> cannot be applied to (java.util.Set<java.lang.Object>,
<anonymous Test.F<java.lang.Integer,java.util.Collection<java.lang.String>>>)
return iopt.option(Collections.emptySet(), new F<Integer, Collection<String>>() {
^
1 error
Now, the following implementation of getColl() works, of course:
public Collection<String> getColl() {
Option<Integer> iopt = null;
Collection<String> empty = Collections.emptySet();
return iopt.option(empty, new F<Integer, Collection<String>>() {
public Collection<String> f(Integer i) {
return Collections.singleton(i.toString());
}
});
}
and the whole intent of the typesafe methods on Collections is to avoid this sort of issue with the singleton collections (as opposed to using the static variables.) So is the compiler simply unable to perform inference across multiple levels of generics? What's going on?
Java needs a lot of hand holding with its inference. The type system could infer better in a lot of cases but in your case the following will work:
print("Collections.<String>emptySet();");
First you can narrow down your problem to this code:
public class Test {
public void option(Collection<String> b) {
}
public void getColl() {
option(Collections.emptySet());
}
}
This does not work, you need a temporary variable or else the compiler cannot infer the type. Here is a good explanation of this problem: Why do temporary variables matter in case of invocation of generic methods?
Collections.emptySet() is not a Collection<String> unless Java knows that it needs a Collection<String> there. In this case, it appears that the compiler is being somewhat silly about the order that it tries to determine types, and tries to determine the return type of Collections.emptySet() before trying to determine the intended template parameter type for B is actually String.
The solution is to explictly state that you need Collections.<String>emptySet(), as mentioned by GaryF.
It looks like a typecasting issue - i.e., that it's being required to cast Object (in Set<Object>, which would be the type of the empty set) to String. Downcasts are not, in the general case, safe.