I want to check that a map contains a certain set of entries. It is allowed to contain other entries that are not in expectedMap. I currently have the following assertion:
assertThat(expectedMap.entrySet(), everyItem(isIn(actualMap.entrySet())));
Although this does work, the failure message that it prints is confusing because the expected and received arguments have been reversed from normal usage. Is there a better way to write it?
hasItems does what you want, but in a non-obvious way. You have to jump through some hoops as Hamcrest has relatively limited support for matching Iterables. (Without going into detail, this is due to the vagaries of how Java generics work- I'll post some more links with detail later).
(I'm assuming you're using generics, e.g. Map<String, String> as opposed to simply Map).
In the meantime you have a couple of options...
If you are happy with test code that raises warnings / using #SuppressWarnings("unchecked") in your test:
assertThat(actualMap.entrySet(), (Matcher)hasItems(expectedMap.entrySet().toArray()));
Explanation: there is no overload of hasItems that takes a Set or Iterable, but it will take an array. Set.toArray() returns Object[], which won't match assertThat against your actualMap.entrySet() - but if you erase the declared type of the Matcher, it will happily proceed.
If you want an assertion that compiles without warnings, it gets uglier - you need to copy the Set into some kind of Iterable<Object> (you can't cast) in order to match on the Objects :
assertThat(new HashSet<Object>(actualMap.entrySet()), hasItems(expectedMap.entrySet().toArray()));
But to be perfectly honest, for clarity you are almost certainly best off asserting each entry individually:
for (Entry<String, String> entry : expectedMap.entrySet()) {
assertThat(actualMap, hasEntry(entry.getKey(), entry.getValue()));
}
...or you could write your own Matcher - there are plenty of resources on how to do this online and on SO.
Related
I'm trying my hand at using Optional as much as I can over normal null checks; and I ran into a suggestion by my IDE (IntelliJ):
Can be replaced by a singular expression in functional style.
Here is the code in question, the line in question:
Entry entry = maybeBytes.isPresent() ? Entry.deserialize(maybeBytes.get()) : new Entry();
I've looked a bit here, but couldn't find a usage or at least see one that would fit my case here. I'm pretty new to the lambda features.
How about:
Entry entry = maybeBytes.map(Entry::deserialize).orElseGet(Entry::new);
If maybeBytes contains a value, it will be passed to the function you provide to map(), and you'll get an Optional<Entry> with the result (and if maybeBytes was empty, you'll get an empty Optional<Entry>). orElseGet() will give you the contents of the Optional<Entry> if it's nonempty, and otherwise, it will give you the result of evaluating the function you pass to it (in this case, the constructor of Entry).
Pressing Alt+Enter when you see such suggestions will apply them, and you'll see the result yourself :)
In my case it was orElse() which fit to my needs. I do not needed to do extra deserialization.
instead of isPresent() combined with get() certainly does the trickā¦
return maybeBytes.isPresent() ? maybeBytes.get() : new Entry();
you can use
return maybeBytes.orElse(new Entry());
Time and again, I find myself in the situation where I want to use a value, and add it to a collection at the same time, e.g.:
List<String> names = new ArrayList<>();
person1.setName(addTo(names, "Peter"));
person2.setName(addTo(names, "Karen"));
(Note: using java.util.Collection.add(E) doesn't work of course, because it returns a boolean.)
Sure, it's easy to write a utility method myself like:
public static <E> E addTo(Collection<? super E> coll, E elem) {
coll.add(elem);
return elem;
}
But is there really not something like this already in JavaSE, Commons Collections, Guava, or maybe some other "standard" library?
The following will work if you use Eclipse Collections:
MutableList<String> names = Lists.mutable.empty();
person1.setName(names.with("Peter").getLast());
person2.setName(names.with("Karen").getLast());
The with method returns the collection being added to so you can easily chain adds if you want to. By using getLast after calling with on a MutableList (which extends java.util.List) you get the element you just added.
Note: I am a committer for Eclipse Collections.
This looks like a very strange pattern to me. A line like person1.setName(addTo(names, "Peter")) seems inverted and is very difficult to properly parse:
An existing person object is assigned a name, that name will first be added to a list of names, and the name is "Peter".
Contrast that with (for example) person1.setName("Peter"); names.add(person1.getName());:
Make "Peter" the name of an existing person object, then add that name to a list of names.
I appreciate that it's two statements instead of one, but that's a very low cost relative to the unusual semantics you're proposing. The latter formatting is easier to understand, easier to refactor, and more idiomatic.
I would be willing to wager that many scenarios that might benefit from your addTo() method have other problems and would be better-served by a different refactoring earlier on.
At its core the issue seems to be that you're trying to represent a complex data type (Person) while simultaneously constructing an unrelated list consisting of a particular facet of those objects. A potentially more straightforward (and still fluent) option would be to construct a list of Person objects and then transform that list to extract the values you need. Consider:
List<Person> people = ImmutableList.of(new Person("Peter"), new Person("Karen"));
List<String> names = people.stream().map(Person::getName).collect(toList());
Notice that we no longer need the isolated person1 and person2 variables, and there's now a more direct relationship between people and names. Depending on what you need names for you might be able to avoid constructing the second list at all, e.g. with List.forEach().
If you're not on Java 8 yet you can still use a functional syntax with Guava's functional utilities. The caveat on that page is a worthwhile read too, even in Java-8-land.
Assume I want to unit test a method with this signature:
List<MyItem> getMyItems();
Assume MyItem is a Pojo that has many properties, one of which is "name", accessed via getName().
All I care about verifying is that the List<MyItem>, or any Iterable, contains two MyItem instances, whose "name" properties have the values "foo" and "bar". If any other properties don't match, I don't really care for the purposes of this test. If the names match, it's a successful test.
I would like it to be one-liner if possible. Here is some "pseudo-syntax" of the kind of thing I would like to do.
assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});
Would Hamcrest be good for this type of thing? If so, what exactly would be the hamcrest version of my pseudo-syntax above?
Thank you #Razvan who pointed me in the right direction. I was able to get it in one line and I successfully hunted down the imports for Hamcrest 1.3.
the imports:
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
the code:
assertThat( myClass.getMyItems(), contains(
hasProperty("name", is("foo")),
hasProperty("name", is("bar"))
));
AssertJ provides an excellent feature in extracting() : you can pass Functions to extract fields. It provides a check at compile time.
You could also assert the size first easily.
It would give :
import static org.assertj.core.api.Assertions;
Assertions.assertThat(myClass.getMyItems())
.hasSize(2)
.extracting(MyItem::getName)
.containsExactlyInAnyOrder("foo", "bar");
containsExactlyInAnyOrder() asserts that the list contains only these values whatever the order.
To assert that the list contains these values whatever the order but may also contain other values use contains() :
.contains("foo", "bar");
As a side note : to assert multiple fields from elements of a List , with AssertJ we do that by wrapping expected values for each element into a tuple() function :
import static org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple;
Assertions.assertThat(myClass.getMyItems())
.hasSize(2)
.extracting(MyItem::getName, MyItem::getOtherValue)
.containsExactlyInAnyOrder(
tuple("foo", "OtherValueFoo"),
tuple("bar", "OtherValueBar")
);
Its not especially Hamcrest, but I think it worth to mention here. What I use quite often in Java8 is something like:
assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));
(Edited to Rodrigo Manyari's slight improvement. It's a little less verbose. See comments.)
It may be a little bit harder to read, but I like the type and refactoring safety.
Its also cool for testing multiple bean properties in combination. e.g. with a java-like && expression in the filter lambda.
Try:
assertThat(myClass.getMyItems(),
hasItem(hasProperty("YourProperty", is("YourValue"))));
Assertj is good at this.
import static org.assertj.core.api.Assertions.assertThat;
assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");
Big plus for assertj compared to hamcrest is easy use of code completion.
As long as your List is a concrete class, you can simply call the contains() method as long as you have implemented your equals() method on MyItem.
// given
// some input ... you to complete
// when
List<MyItems> results = service.getMyItems();
// then
assertTrue(results.contains(new MyItem("foo")));
assertTrue(results.contains(new MyItem("bar")));
Assumes you have implemented a constructor that accepts the values you want to assert on. I realise this isn't on a single line, but it's useful to know which value is missing rather than checking both at once.
AssertJ 3.9.1 supports direct predicate usage in anyMatch method.
assertThat(collection).anyMatch(element -> element.someProperty.satisfiesSomeCondition())
This is generally suitable use case for arbitrarily complex condition.
For simple conditions I prefer using extracting method (see above) because resulting iterable-under-test might support value verification with better readability.
Example: it can provide specialized API such as contains method in Frank Neblung's answer. Or you can call anyMatch on it later anyway and use method reference such as "searchedvalue"::equals. Also multiple extractors can be put into extracting method, result subsequently verified using tuple().
Alternatively to hasProperty you can try hamcrest-more-matchers where matcher with extracting function. In your case it will look like:
import static com.github.seregamorph.hamcrest.MoreMatchers.where;
assertThat(myClass.getMyItems(), contains(
where(MyItem::getName, is("foo")),
where(MyItem::getName, is("bar"))
));
The advantages of this approach are:
It is not always possible to verify by field if the value is computed in get-method
In case of mismatch there should be a failure message with diagnostics (pay attention to resolved method reference MyItem.getName:
Expected: iterable containing [Object that matches is "foo" after call
MyItem.getName, Object that matches is "bar" after call MyItem.getName]
but: item 0: was "wrong-name"
It works in Java 8, Java 11 and Java 14
With Stream you can also do:
List<String> actual = myList.stream().map(MyClass::getName).collect(toList());
assertThat(actual, hasItem("expectedString1"));
Because with anyMatch() or allMatch(), you know some values in your list are in the list, but there is possibility that your actual list only contains 5 values while in anyMatch() you have 6; you don't know if all values are present or not. With hasItem(), you indeed check every value you want.
There's no doubt that in-out parameters leads to confused code since they may increase unexpected/unpredictabled side-effects.
So, many good programmers say :
Avoid in-out parameters for changing mutable method parameters. Prefer to keep parameters unchanged.
For a perfectionist programmer who expects his code to be the most clean and understandable, does this "rule" must be applied in all case ?
For instance, suppose a basic method for adding elements to a simple list, there's two ways :
First way (with in-out parameter):
private void addElementsToExistingList(List<String> myList){
myList.add("Foo");
myList.add("Bar");
}
and the caller being :
List<String> myList = new ArrayList<String>();
//.......Several Instructions (or not) .....
addElementsToExistingList(myList);
Second way without out parameter :
private List<String> addElementsToExistingList(List<String> originalList){
List<String> filledList = new ArrayList<String>(originalList); //add existing elements
filledList.add("Foo");
filledList.add("Bar");
return filledList;
}
and the caller being :
List<String> myList = new ArrayList<String>();
//.......Several Instructions (or not) .....
myList.addAll(addElementsToExistingList(myList));
Pros of second way :
Parameter are not modified => no risk of unexpected side-effects for a new code reader.
Cons of second way :
Very verbose and very less readable ...
Of course, you would tell me that for a code as simple as this one, first way is really more convenient.
But, if we don't consider the difficulty of any concept/code, I juge the second way more logical and obvious for any readers (beginners or not).
However, it violates the CQS principle that consider "command" methods having void return with potential (but allowed since it's the convention) side-effects and "query" methods having a return type and without side-effects.
So, what should a motivate programmer adopt ? Mix of two accorging to the code case ? Or keep the "law" expecting to always avoid in-out parameters...
(Of course, method for adding Element is named for expliciting the example, and would be a bad name choice in real code).
I think the law should be:
Use what is more straight-forward, but always, always document the behavior of your methods extensively.
Your second example is a very nice case where without documentation you would have a guaranteed bug: the name of the method is addElementsToExistingList, but the method does not add elements to the existing list - it creates a new one. A counter-intuitive and misleading name, to say the least...
There is a third way. Wrap List<String> into a class that knows how to add elements to itself:
class ElementList {
private List<String> = new ArrayList<String>();
public void addElements(Element... elements);
}
I like this approach because it keeps the List implementation private. You don't have to worry if someone passes an immutable list to your method or whether parameters are modified. The code is simpler. Long method names like addElementsToExistingList are code smells that an object is trying to do something another object should be doing.
You should always document when mutating an object that is a parameter because otherwise this can have unintended side effects for the caller. In the first case I agree with the others that have commented that the method name is sufficient documentation.
In your second example, the elements that are already present in myList seem to be added twice. In fact you could entirely remove the parameter of the addElementsToExistingList method and rewrite it as:
private List<String> getElements() {
List<String> filledList = new ArrayList<String>();
filledList.add("Foo");
filledList.add("Bar");
return filledList;
}
List<String> myList = new ArrayList<String>();
//.......Several Instructions (or not) .....
myList.addAll(getElements());
Note that this code is not equivalent to your second example because the elements are only added once, but I think this is actually what you intended. This is the style that I usually prefer. This code is easier to understand and more flexible than the first example without adding extra code (it may degrade performance very slightly but this usually isn't a concern). The client of getElements() can now also do other things with the element list besides adding it to an existing collection.
It's fine to change/mutate parameters as long as it's documented. And of course with a method name of "addElementsToExistingList", what else should someone expect? However, as someone previously pointed out, your second implementation returns a copy and doesn't modify the original, so the method name is now misleading. Your first way is a perfectly acceptable way of doing things. The only other additional improvements is to possibly add a true/false value to the return indicating true if only all the elements were added to the list.
In the case of your example the name makes it clear - "addElementsToExistingList" to me seems pretty clearly to hint that you're going to .. er.. you know. But your concern would be justified with a less obvious name.
For example, in ruby this is commonly handled with naming conventions
"a".upcase => gives you the uppercase of the variable, leaves the original unchanged
"a".upcase! => alters the original variable
I'd like to have a Map that is also a Collection. Or more specifically, I'd like to be able to iterate over the entries in a Map, including the case where there are multiple entries for a particular key.
The specific problem I'm trying to solve is providing an object that can be used in jstl both to iterate over using c:forEach and in an expression like ${a.b.c}. In this example, I'd want ${a.b.c} to evaluate to the the first value of c (or null if there are none), but also be able to iterate over all cs with <c:forEach items="${a.b.c}"> and have the loop body see each individual value of c in turn, although they have the same key in the Map.
Looking at things from a method point of view, this should be straightforward, just provide a Map implementation whose entrySet() method returns a set with multiple Entries with the same key. But since this seems to violate the contract of a Map, will things break in subtle yet disastrous ways? Has anyone else done this sort of thing?
(If you guessed I'm trying to present xml, you'd be correct)
EDIT
Please note that this is for use in jstl, so whatever interface I present must meet 2 conditions:
for use with the [] and . operators, it must be a Map, List, array or JavaBeans object (and of those it can't be a List or array because the indexes will not be numbers)
for use with forEach it must be an array, Collection, Iterator, Enumeration, Map, or String.
So I guess the real question is, can I count on jstl only calling .containsKey(), .get(), and .entrySet() and not caring about invariants being violated, and not internally making a copy of the Map which would not preserve the special iteration behavior.
What you are looking for is a Multimap. Guava provides an implementation of it and specifically you are looking for ArrayListMultimap.
I barely remember jstl, but what you're saying sounds a kind of controversial:
In foreach:
here ${a.b.c} should point to some container of values and then we iterate over it.
On the other hand you say, ${a.b.c} "should evaluate to the the first value of c" (or null...)
Its an ambiguous definition.
If you feel like Multimap is not what you want, you can provide your own collection implementation (probably internally based on Multimap)
Just as an idea you can always look at a single element as a list (that accidentally
is comprised of one element). This way you would resolve your ambiguity, I guess.
I hope this helps
Having a Map with multiple entries for the same key irreparably breaks the Map contract. If Multimap doesn't work for you, then there's no way to do this without breaking a lot of things.
Specifically, if you pass your monstrosity to something that's specified to take a Map, it'll almost certainly break...and it sounds like that's what you want to do with it, so yeah.
how about you use a Map with Collections as values? then you can have different values for the same key and you can iterate over them by a nested foreach-loop
you can also easily write a wrapper for an existing map-implementation, which gives you a single iterator over all values, if you need it that way