Hamcrest Collection matching where objects in list have a field that matches values in another list - junit5

I have a method under test which is returning a list of objects... such as a "Person" object.
I have a list of "expectedLastNames" to validate the result against.
I currently have a working test that loops through the names in "expectedLastNames" and asserts that each is contained in the list of "Person" objects. Like so (be advised the following code snippet is in Kotlin):
expectedLastNames.forEach { expectedLastName ->
assertThat(listOfPeople, hasItem(hasProperty("lastName", `is`(expectedLastName.trim()))))
}
This works great when the assertion passes, and validates my method. However, it is extremely cumbersome when the test fails, because it fast-fails as soon as it encounters a name that is missing and doesn't assert the rest of the names. I would prefer to improve my assertion to report ALL missing names all at once instead of one at a time.
Something along the lines of:
assertThat(expectedLastNames, allItems(lastNameMatches(listOfPeople)));
I want my assertionFailure message to be something like:
Expected a list matching all of "Smith, Jones, Davis", but ["Smith", "Davis"] were not found in "Person[firstName=John, lastName="Jones"], Person[firstName=Sarah, lastName=Jones]"
If there are additional Person objects in the results with lastName values that are not included in "expectedLastNames", this should still pass as long as ALL of the names in "expectedLastNames" are represented in the results.
It is also important to know that this is a Junit5 parameterized test, and the "expectedLastNames" is one of the parameters that are being passed in. So I cannot just hard-code the last names into the assertion.
Is there a Hamcrest collection matcher that will do what I want? Or if not, can anybody get me started with a custom matcher that will do it?
Any help is appreciated.

You can do that with the provided hamcrest matchers for Iterable:
import org.junit.Test;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
public class MatchValuesInAnotherListTest {
static class Person{
private final String firstName;
private final String lastName;
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
#Test
public void matchLastNames() {
List<Person> listOfPeople = asList(new Person("Paul", "Smith"), new Person("John", "Davis"));
assertThat(listOfPeople.stream().map(Person::getLastName).collect(toList()), containsInAnyOrder("Davis", "Smith"));
assertThat(listOfPeople.stream().map(Person::getLastName).collect(toList()), contains("Smith", "Davis"));
}
}
They will provide a nicely formatted failure message:
java.lang.AssertionError:
Expected: iterable over ["Smith"] in any order
but: Not matched: "Davis"
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)

Related

Is there a way to search all fields with redis-om-spring using the EntityStream API?

In redis-om-spring I can search all fields by simply adding a search method to the repository.
public interface ProductRepository extends RedisDocumentRepository<Product, String> {
Page<Product> search(String text, Pageable pageable);
}
When using the EntityStream, I can search on specific fields, but not across all fields.
var result = entityStream.of(Product.class)
.anyMatch(new StartsWithPredicate<>(Product$.UNIQUE_ID.getField(),"100790"))
#AllArgsConstructor
public class Product{
#Id
String uniqueId;
#Searchable
String field1;
#Searchable
String field2;
#Searchable
String fieldN;
}
repo.save(new Product("UA","searchForA1","searchForA2","searchForAN");
repo.save(new Product("UB","searchForB1","searchForB2","searchForBN");
repo.save(new Product("UC","searchForC1","searchForC2","searchForCN");
I need to search across all fields. Am I missing something in the EntityStream API or is this not possible?
Something that generates:
FT.SEARCH my-idx "thesearchTerm"
Yes, there is a filter method in the SearchStream interface that takes a free-form text String:
SearchStream<E> filter(String freeText);
See https://github.com/redis/redis-om-spring/blob/main/redis-om-spring/src/main/java/com/redis/om/spring/search/stream/SearchStream.java#L20

How to collect a stream into a CopyOnWriteArrayList

I'm getting "Incompatible types, required: CopyOnWriteArrayList, found: Object" with the following. I'm using IntelliJ 2016.1.1.
CopyOnWriteArrayList<Foo> l = fields.stream()
.distinct()
.collect(toCollection(CopyOnWriteArrayList::new));
The problem is that fields has an inappropriate type, most likely, it has a raw type, which will turn the generic invocations of the Stream chain into unchecked operations returning their erased type, which is Object for the terminal collect call.
Using the right type, this works without problems, i.e.
List<String> fields=Arrays.asList("foo", "bar", "baz", "foo");
CopyOnWriteArrayList<String> l =
fields.stream()
.distinct()
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
works. But note that building a CopyOnWriteArrayList this way is rather expensive as the name “copy on write” already suggests. Copying the entire contents on each insertion leads to quadratic time complexity.
The solution is to collect into a temporary collection, better suited to incremental building, before converting to the desired target type. That copying step might look like overhead, but it’s linear overhead, compared to the quadratic complexity of collecting directly into the CopyOnWriteArrayList.
CopyOnWriteArrayList<String> l =
fields.stream()
.distinct()
.collect(Collectors.collectingAndThen(
Collectors.toList(), CopyOnWriteArrayList::new));
Note that in this specific case, distinct implicitly builds a Set behind the scenes, so we can improve the performance by building the Set explicitly in place of the temporary List and remove the distinct step:
CopyOnWriteArrayList<String> l =
fields.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(LinkedHashSet::new),
CopyOnWriteArrayList::new));
which leads to the conclusion that for this specific use case, we can have it all simpler and potentially even more efficient:
CopyOnWriteArrayList<String> l = new CopyOnWriteArrayList<>(new LinkedHashSet<>(fields));
It seams like your fields object is not of type Foo , otherwise it should work find below working code.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
public class Foo {
private String name;
Foo(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Foo [name=" + name + "]";
}
public static void main(String[] args) {
List<Foo> fields = new ArrayList<>();
fields.add(new Foo("aa"));
fields.add(new Foo("bb"));
CopyOnWriteArrayList<Foo> l = fields.stream().distinct().collect(Collectors.toCollection(CopyOnWriteArrayList::new));
System.out.println("l"+l);
}
}
PS:If your fields is non generic then also this will give error

Having trouble extracting a common interface

I would like to create a calculator application that can switch between different number bases. As far as entering digits is concerned, I was thinking the following would be a flexible api:
public interface ICalculator
{
string Enter(INumberElement element);
}
public class BaseTenCalculator : ICalculator
{
public string Enter(INumberElement element)
{
...
}
}
public class BaseTwoCalculator : ICalculator
{
public string Enter(INumberElement element)
{
...
}
}
My problem is that for the BaseTenCalculator, I would like a method
Enter(BaseTenNumberElement element)
and for a BaseTwoCalculator, I would like a method
Enter(BaseTwoNumberElement element)
to make sure only valid digits for that number base get entered. However, the only way I can think of enforcing this constraint is downcasting the 'element' argument in the two different implementations, and throwing an exception if INumberElement is not of the correct type. I feel like this is 'wrong', and I'm missing something. Is there another way? Is it even possible to create a common interface for two different number base calculators?
public interface ICalculator<in T> where T : INumberElement
{
string Enter(T element);
}
public class BaseTenCalculator : ICalculator<BaseTenNumberElement>
{
public string Enter(BaseTenNumberElement element)
{
throw new NotImplementedException();
}
}
public class BaseTwoCalculator : ICalculator<BaseTwoNumberElement>
{
public string Enter(BaseTwoNumberElement element)
{
throw new NotImplementedException();
}
}
I think you're thinking of the problem incorrectly. A number is a number regardless of base. Base is only a visible representation of the number. A good example to work from might be BigInteger. It has a constructor: BigInteger(String val, int radix), and a function: toString(int radix). All the work of representing the number is done the same. The only thing that differs is parsing from a string representation into the number, and then getting back out into a number format in a particular base.
You could create a multi-base calculator by using BigInteger or BigDecimal underneath and just using a base selection to set the radix value to parse or print the number(s). You'd also want to limit the input buttons (assuming you're using buttons), but that's really just a counting problem.

Providing Jackson Mapper multiple ways to deserialize the same object

I'm trying to deserialize two types of json:
{
name: "bob",
worksAt: {
name: "Bobs department store",
location: "downtown"
},
age: 46
}
and
{
name: "Tom",
worksAt: "company:Bobs department store",
age: 27
}
into these objects:
The first way creates two new objects, the second way requests the object from the database based on the contents of a string.
sort of like how jackson mapper can deserialize an arbitrary string into an object, for objects like this:
public class Company{
public String name;
public Employee[] employees
public Company(){}
public Company(String json){
//turn string into object using whatever encoding you want blah blah blah...
}
}
The trouble is I need both. I need it to handle objects and strings. Both could arrive from the same input.
The first think I tried was making a Converter
It says these create a delegate type to pass to the deserializer, but the converter is always applied even when the datatype isn't a string. So that didn't work.
I've also tried a normal deserializer, but I can't find a way to defer to the BeanDeserializer. The beanDeserializer is so complicated that I can't manually instantiate it. I also see no way to defer to a default deserializer in jackson mapper.
Do I have to re-implement jackson mappers deserialization to do this? Is there any way for a deserializer to say "I can't do this, use the default implementation."?
Edit: Some further progress. Based on the Jackson Mapper source code, it looks like you can instatiate bean deserializers like this:
DeserializationConfig config = ctxt.getConfig();
JavaType type = config.constructType(_valueClass);
BeanDescription introspect = config.introspect(type);
JsonDeserializer<Object> beanDeserializer = ctxt.getFactory().createBeanDeserializer(ctxt, type , introspect);
but for some reason all the _beanProperties have the FailingDeserializer set for their _valueDeserializer and the whole thing fails. So I have no idea why that happens...
Have you tried writing a custom deserializer? This gives you the most control on how Jackson deserializes the object. You may be able to try to deserialize one way, and if there's an error, try another way.
Jackson can also handle polymorphic deserialization, though this would require a small change to the json to include type information, and it sounds like your problem constraints might not allow that.
If I understand the problem correctly, I would recommend using JsonNode. You can define a setter in your top-level type like this:
setWorksAt(JsonNode node) {
if (node.getNodeType == JsonNodeType.STRING) {
String name = node.getText();
name = name.substring(name.lastIndexOf(':'));
this.company = new Company(name);
} else if (node.getNodeType == JsonNodeType.OBJECT) {
this.company = mapper.treeToValue(node, Company.class);
}
}
That allows you to handle the two separate worksFor inputs, while still allowing the standard mapper to handle any substructures for the OBJECT case.
With recent versions of Jackson (2.8+ I think, definitely works with 2.9) you can use multiple #JsonCreator and do something like this:
public class Company {
private String name;
private String location;
private Company(String name, String location) {
this.name = name;
this.location = location;
}
private Company(String stringRepresentation) {
// add code here to parse string and extract name and location
}
#JsonCreator
private static Company fromJson(
#JsonProperty("name") String name,
#JsonProperty("location") String location)
{
return new Company(name, location);
}
#JsonCreator
private static Company fromJson(String str) {
return Company(str);
}
}

Hibernate/Spring taking out class mapping. About reflection

Im trying to write an aplication with uses hibernate to write to database, however in some actions i have to use JDBC on data in tables made by HB.
JDBS is requred to give administrator ability to create SQL queries with will return statistic info about data in database like number of processed document of specified type, numbers of success/failed log in attempts or total value of products in orders.
To do that i've done an from that allows to create class that has override toString() with return nice sql query string.
All works but now im trying to make administrator live easier by hiving him an ability to choose of table/column names. And here is an problem, because they are created by hibernate. some by #column annotation other by field name.
How can i check how field mapping?
I know its all about reflections but didnt do much of that in java yet.
example
#Entity
#Table(name= "my_table_name" )
public class TableOFSomething implements Serializable{
//This field isn't mapped into database and info about it is not requred.
//In fact, info about it may cause an error.
private static final long serialVersionUID = 7L;
#Id
#Column(name="id")
private String id;
private String fieldOne;
#Column(name="field_two")
private String fieldTwo;
#Column(name="renamed_just_for_fun")
private int Number;
//code with getters & setters
}
How to write methods that will have definition like
public <T> String tableName(Class<T> Target); //returns name of table in database
public <T> ArrayList<String> tabelFields(Class<T> Target); //returns name of fields in database
Hibernate has API - getClassMetadata that can explore the mapping. The API might change and is now located in another place , but i will use it and not in reflection for this.
look on this post for more details:
Get the table name from the model in Hibernate
if you want reflection , so use this link
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import javax.persistence.Column;
import javax.persistence.Table;
import odi.beans.statistic.QueryBean;
public class ReflectionTest {
public static void main(String[] args) {
ReflectionTest test=new ReflectionTest();
System.out.println("Table name of "+QueryBean.class.getName()+" is "+test.getTableName(QueryBean.class));
System.out.println("Column names in this table are:");
for(String n: test.getColumnNames(QueryBean.class)){
System.out.println("\t"+n);
}
System.out.println("Good bye ;)");
}
public <T> ArrayList<String> getColumnNames(Class<T> target) {
ArrayList<String> ret=new ArrayList<>();
Field[] fields = target.getDeclaredFields();
String fieldName =null;
for (Field f : fields) {
//jump to next if if field is static
if (Modifier.isStatic(f.getModifiers()))
continue;
if (f.isAnnotationPresent(Column.class)) {
Column a = f.getAnnotation(Column.class);
fieldName = a.name();
} else {
fieldName = f.getName();
}
ret.add(fieldName);
}
return ret;
}
public <T> String getTableName(Class<T> target){
String ret=target.getSimpleName();
if (target.isAnnotationPresent(Table.class))
{
Table t=target.getAnnotation(Table.class);
ret=t.name();
}
return ret;
}
}
Is it cover all possibilities?
I know now that Hibernate way would be easier, but this is also about learning of very useful reflection mechanism :)
EDIT:
Important question:
Will this work only on annotations or also on xml mapping?