Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Please add support for containsInAnyOrder(T... items) #37

Closed
mcbeelen opened this issue Nov 11, 2017 · 6 comments
Closed

Please add support for containsInAnyOrder(T... items) #37

mcbeelen opened this issue Nov 11, 2017 · 6 comments

Comments

@mcbeelen
Copy link

http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#containsInAnyOrder(T...)

assertThat(listOf("foo", "bar"), containsInAnyOrder("bar", "foo"))

@npryce
Copy link
Owner

npryce commented Jan 2, 2019

This is the same as

assertThat(listOf("foo", "bar"), List<String>::containsAll, listOf("bar", "foo"))

You can turn the method reference and a list of expected elements to a Matcher with the expression Matcher(List<String>::containsAll, expectedElementsList)

A factory function that is overloaded for varargs is a one-liner.

There are many, many methods that make useful Hamkrest matchers. Far too many to support them all in the library. The Matcher function is provided to make it as easy as possible to turn methods into matchers that are useful for your project.

@npryce npryce closed this as completed Jan 2, 2019
@MalteHartwig
Copy link

Is there not a difference if the collection under test contains more elements than the specified expectation? containsInAnyOrder expicitly states that it "Matches an Iterable that contains exactly the elements of the given collection in any order", while containsAll returns true if any subset of elements is passed.

Hence, a custom util Matcher would have to be a bit more complicated than List::containsAll. Would this qualify containsInAyOrder to be added to the library?

@markslater
Copy link

As @MalteHartwig points out, unfortunately this passes:

assertThat(listOf("foo", "bar"), List<String>::containsAll, emptyList())

It's possible to get closer to containsInAnyOrder by adding an expression to match on List size, provided the expected list has no duplicates, but by this point, you're outside the realm of a simple in-line expression.

Unless, that is, someone can come up with some inspired use of the standard library for doing this.

@corbym
Copy link

corbym commented Nov 11, 2020

you could add an extension function to List:

fun <T> List<T>.containsInAnyOrder(elements : List<T>) : Boolean =
     elements.size == this.size && elements.distinct().all { it: T ->
        this.contains(it)
    }

and use that:

assertThat(listOf("a", "b", "d", "c"), List<String>::containsInAnyOrder, listOf("d", "a", "b", "c")) -- matches

assertThat(listOf("a", "b", "d", "c"), List<String>::containsInAnyOrder, listOf("d", "e", "f")) -- does not match

assertThat(listOf("a", "b", "d", "c"), List<String>::containsInAnyOrder, listOf()) -- does not match

(not fully tested against the hamcrest version)

However it would be nice to have this functionality built in.

@markslater
Copy link

@corbym re:

fun <T> List<T>.containsInAnyOrder(elements : List<T>) : Boolean =
     elements.size == this.size && elements.distinct().all { it: T ->
        this.contains(it)
    }

Bear in mind that this doesn't work (at least not in the way I would expect) with some cases of lists with duplicate elements, for example:

assertThat(listOf("a", "a", "b"), List<String>::containsInAnyOrder, listOf("a", "b", "b")) -- matches[!]

I propose this implementation to handle the case where there are duplicates:

fun <T> List<T>.containsInAnyOrder(elements: List<T>) =
        groupingBy { it }.eachCount() == elements.groupingBy { it }.eachCount()

@corbym
Copy link

corbym commented Aug 11, 2021

@corbym re:

fun <T> List<T>.containsInAnyOrder(elements : List<T>) : Boolean =
     elements.size == this.size && elements.distinct().all { it: T ->
        this.contains(it)
    }

Bear in mind that this doesn't work (at least not in the way I would expect) with some cases of lists with duplicate elements, for example:

assertThat(listOf("a", "a", "b"), List<String>::containsInAnyOrder, listOf("a", "b", "b")) -- matches[!]

I propose this implementation to handle the case where there are duplicates:

fun <T> List<T>.containsInAnyOrder(elements: List<T>) =
        groupingBy { it }.eachCount() == elements.groupingBy { it }.eachCount()

I expect it depends on how you interpret the hamcrest version (if, indeed, you care about such parity). The documentation states:

Creates an order agnostic matcher for Iterables that matches *when a single pass* over the examined Iterable yields a series of items, each satisfying *one matcher anywhere in the specified matchers*. 

For a positive match, the examined iterable must be of the same length as the number of specified matchers.

N.B. each of the specified matchers will only be used once during a given examination, *so be careful when specifying matchers that may be satisfied by more than one entry in an examined iterable.*

-- http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#containsInAnyOrder(T...)

.. by this I would expect to get "true" back for your example a,a,b containsInAnyOrder a,b,b (as is the case).

Of course I'd have to examine the implementation of the hamcrest version to be sure.

However, it's your library so feel free to give whatever recommended solution you feel is right 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants