jocarreira / hamcrest

Automatically exported from code.google.com/p/hamcrest
0 stars 0 forks source link

Count elements in collections? #51

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Hi!

It would be nice to have counting possibilities (meaning count elements in 
collections that match certain criterias).
Is this something you are planning on adding? 
I haven't seen anything similar when browsing the source code?

I would like to write code like this:
  assertThat(list, hasExactly(2).elements(greaterThan(3)));

I'm attaching a prototype implementation of what I would like to see.

Best regards!
/Patrik

Original issue reported on code.google.com by patrik.h...@gmail.com on 23 Oct 2008 at 9:37

Attachments:

GoogleCodeExporter commented 8 years ago
Sounds like a useful feature.  I think the expected count could also be given 
as a
matcher.  For example:

assertThat(list, hasCountOfElements(greaterThan(2), not(equalTo(0))));

Original comment by nat.pr...@gmail.com on 24 Oct 2008 at 8:51

GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago

Original comment by nat.pr...@gmail.com on 24 Oct 2008 at 12:34

GoogleCodeExporter commented 8 years ago
I think the semantics of a call like 
  hasCountOfElements(greaterThan(2), not(equalTo(0))) 
can be a bit confusing. 

It could be interpreted as 
  the count of elements that have a value greater than 2 is not equal to 0
or it could be interpreted as
  the count of elements is greater than 2 for elements with a value not equal to 0.

I think it reads better with
  hasAtLeast(1).elements(greaterThan(2))
or
  hasAtLeast(3).elements(not(equalTo(0))

But this is purely academic. It would be easy enough to provide several 
variants of 
factory methods to suit different needs. I'm attaching an example 
implementation.

Original comment by patrik.h...@gmail.com on 24 Oct 2008 at 2:55

Attachments:

GoogleCodeExporter commented 8 years ago
The issue with chained methods is that they only work when later clauses are 
optional.

What would hasAtLeast(1) mean?

Original comment by nat.pr...@gmail.com on 24 Oct 2008 at 3:16

GoogleCodeExporter commented 8 years ago
After having thought more of this I think it would be more flexible to add
a few factory methods to the org.hamcrest.number.OrderingComparisons class.
These could then be used to build more expressive statements.
  exactly(T value)
  atLeast(T value)
  atMost(T value)
  between(T min, T max)

Also I think the naming 'has' was a poor choice. Don't you think 'contains' is 
far better since it better reveals the intention of the interface? So I would 
like 
to see (perhaps a modification to org.hamcrest.core.IsCollectionContaining 
class?)
an interface I can use like this, where I can specify the number of elements to 
match:

  final Collection<Integer> integers = Arrays.asList(3, 6, 4, 2, 3, 7, 1, 9);
  assertThat(integers, contains(between(4, 9)).elements());   // Just count the 
elements
  assertThat(integers, contains(atLeast(2)).elements(equalTo(3)));  // Count matching 
elements

  final Collection<Number> numbers = Arrays.<Number>asList(4, 3.2, null, 0, 7.9);
  assertThat(numbers, contains(exactly(1)).elements(nullValue())); // Generics to 
allow mixing of types
  assertThat(numbers, contains(atLeast(2)).elements(instanceOf(Double.class)));

I am not sure I understand why the later clauses need to be optional?
Meaning the first call need to be a matcher in itself? I would consider the
first call returning a factory for matchers, rather than a matcher in itself, 
and would be satisfied with that. Am I missing something?

Original comment by patrik.h...@gmail.com on 16 Nov 2008 at 6:26

GoogleCodeExporter commented 8 years ago
Any chance of any of this being implemented?

Original comment by baker.st...@gmail.com on 22 Jun 2010 at 10:47

GoogleCodeExporter commented 8 years ago
I just posted a similar discussion to the developers mailing list with respect 
to counting nodes matching an XPath in a DOM. Mapping what I had done before 
switching to Hamcrest to Java, I provided these matchers:

    assertThat(dom,
               hasXPath('book/title')
                       .withContent(stringContaining('JUnit'))
                       .withCount(atLeast(5)));

hasXPath() returns a Matcher and thus withContent() and withCount() are each 
optional factories that return Matcher compositions. It was tricky to get the 
logic to work so you could use both or either one.

The main problem I see with this design is that each type of object (array, 
collection, iterable, query result, etc) needs to implement these factory 
methods. Would it instead be possible to create a generic set of factory 
methods that could be applied to those Matchers that implement extra 
interface(s)?

What about introducing new aggregators that operate on the actual value and 
return a value that gets passed to the matcher?

    assertThat(dom, 
               withXPath('book/title'), 
               withContent(stringContaining('JUnit')),
               countItems(),
               atLeast(5));

or maybe

    assertThat(
         count(dom, 
               withXPath('book/title'), 
               withContent(stringContaining('JUnit'))
         ),
         atLeast(5));

Ah, the Wiki lists hamcrest-collections that implements select(), reject(), 
map(), reduce(), et al: 
http://code.google.com/p/hamcrest-collections/wiki/GettingStarted

    assertThat(
         select(dom, 
                withXPath('book/title'), 
                withContent(stringContaining('JUnit'))  // not exactly
         ).size(),
         atLeast(5));

It's still missing the concept of composing multiple matchers into a chain that 
produces the final list of values to be counted, so above hasXPath() and 
withContent() would need to be merged into a single matcher somehow.

For example the above is not and(withXPath(), withContent()). It could be, but 
better would be to have withXPath() limit the list and *then* for withContent() 
to limit that new list. Of course that's only necessary because of the XPath 
library I'm using.

Original comment by dharkn...@gmail.com on 27 Jul 2010 at 10:18

GoogleCodeExporter commented 8 years ago
tagging

Original comment by t.denley on 12 May 2012 at 11:02