Search code examples
testinggroovyspock

Match against element properties in a collection or array


I'm running a test in Spock, it comes back as an object. The first part of the object is the count of how many results the object has, and then it has each result which includes the name.

I can easily compare the name to the test value if there is one result, but if there are multiple results, how do I compare them?

then: "Container Results"
if (result.containingCount > 1) {
  result.containing[0].name == contains || result.containing[1].name == contains
}
else {
  result.containing[0].name == contains
}

in the third line the comparison is happening with 2 as the containingCount, but what if the number is 3, 5 or even 10? It would seem that there is a much better way.


Solution

  • Your question is not about Spock in particular, but rather about how to effectively work with arrays and collections. As your sample code is incomplete and I do not know if containing is an array, a list or a set, I am showing you how to deconstruct all those types the same way, using a combination of the spread-dot operator *. and the contains method:

    result.containing*.name.contains(contains)
    

    This works for both single- and multi-element arrays and containers. There is no need to rely on if-else, indices or even knowing the container size.

    Because your naming is quite unfortunate, I renamed containing to persons and contains to expectedName in this MCVE:

    package de.scrum_master.stackoverflow.q76240226
    
    import spock.lang.Specification
    
    class CollectionTest extends Specification {
      def "#resultType contains a person named Jane"() {
        given:
        def expectedName = "Jane"
    
        when:
        def result = new Result(persons)
    
        then:
        result.persons*.name.contains(expectedName)
    
        where:
        resultType             | persons
        "single-element array" | [new Person("Jane")] as Person[]
        "single-element list"  | [new Person("Jane")] as List<Person>
        "single-element set"   | [new Person("Jane")] as Set<Person>
        "multi-element array"  | [new Person("Adam"), new Person("Beatrice"), new Person("Jane")] as Person[]
        "multi-element list"   | [new Person("Adam"), new Person("Beatrice"), new Person("Jane")] as List<Person>
        "multi-element set"    | [new Person("Adam"), new Person("Beatrice"), new Person("Jane")] as Set<Person>
      }
    }
    
    package de.scrum_master.stackoverflow.q76240226
    
    import groovy.transform.Canonical
    
    @Canonical
    class Person {
      String name
    }
    
    package de.scrum_master.stackoverflow.q76240226
    
    import groovy.transform.Canonical
    
    @Canonical
    class Result {
      // This should be more specific, e.g. Person[], List<Person>, Set<Person>.
      // We choose Object only for the test's sake to demonstrate that 'contains' works
      // for all those types.
      Object persons
    }
    

    Try it in the Groovy Web Console.

    In your IDE, the test result should be reported similar to this:

    IntelliJ IDEA test result