1

I have this code in Spock:

 then:
    1 * dao.getByValue(Something.ONE, _ as String) >> {Something smth, String value ->
      return createSomething(smth).withValue(value).build()
    }

It doesn't look exactly like that, but you get the point. I want to return an object based on arguments passed to the method, in the real version this object is loaded from database.

The point is that I have this call in a lot of places and it looks exactly the same everywhere. Could I somehow extract this closure and use it everywhere, like this:

then:
    1 * dao.getByValue(Something.ONE, _ as String) >> Closures.makeSomething

I tried using Intellij extract feature, but it kinda went crazy there with types, after I edited the types manually I had weird errors:

public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
  return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'mypackage.MySpec$__clinit__closure1@1757cd72' with class 'mypackage.MySpec$__clinit__closure1' to class 'java.util.Optional'

Even that one didn't work, and I thought it would:

public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
  return createSomething(smth).withValue(value).build()
}
...
1 * dao.getByValue(Something.ONE, _ as String) >> {args -> makeSomething.call(args[0], args[1]) }
groovy.lang.MissingMethodException: No signature of method: mypackage.MySpec$__clinit__closure2.call() is applicable for argument types: (java.util.Arrays$ArrayList) values: [[mypackage.Something$$Lambda$6/1105423942@6f45df59, ...]]

I'm not good at Groovy or Spock in general, I'm just trying this out for now.

Edit:

Working code after @tim_yates suggestion (whole interaction is in the helper method):

then:
  interaction {
    somethingCall(2, Something.TWO)
    somethingCall(3, Something.ONE)
  }
}

private void somethingCall(int times, Something something) {
    times * dao.getByValue(something, _ as String) >> { Something smth, String value ->
        return createSomething(smth).withValue(value).build()
    }
}

Not working code that I'd like (only the return value is in the helper method):

then:
  2 * dao.getByValue(Something.TWO, _ as String) >> makeSomething
  3 * dao.getByValue(Something.ONE, _ as String) >> makeSomething
}

public static final Closure<Optional<Something>> makeSomething = { Something smth, String value ->
  return createSomething(smth).withValue(value).build()
}

If I simply inline each >> makeSomething and write there it's body instead, then it works.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
Shadov
  • 5,421
  • 2
  • 19
  • 38
  • 1
    Like this? https://stackoverflow.com/questions/37830936/spock-extracting-interactions-to-method hope that helps – tim_yates May 02 '18 at 13:54
  • Hm, that is quite a different approach (extracting the whole interaction to separate method, not just it's return - that's what I wanted), but it works, thanks :) You can write an answer here, I will accept if noone else posts anything. – Shadov May 02 '18 at 14:00
  • I do not understand your comment. What is different and what do you mean by "extracting the whole interaction"? Please edit your question in order to show what you have so far, and please add a little more meat to the skeleton. Then Tim or I can maybe post something more concrete for you to optimise it into something you like better. – kriegaex May 02 '18 at 14:10
  • I added an edit to the question. You may not understand what I'm asking about, because it may be a complete nonsense. I started writing in this language just recently and found some seriously powerful things that would never be possible in Java, and now I may have too big expectations of closures :) – Shadov May 02 '18 at 14:30

1 Answers1

0

You have a conceptual problem here. You cannot just split the closure from the preceding code because if you look at it

dao.getByValue(something, _ as String) >> { Something smth, String value ->
  return createSomething(smth).withValue(value).build()
}

you might notice that smth and value inside the closure get their values from getByValue(something, _ as String).

Now if you factor out the in-line closure part into a stand-alone closure instance, you lose that connection. First of all, >> makeSomething has no parameters, secondly you do not evaluate the makeSomething closure, i.e. on the right hand side you do not get your Optional instance but a Closure instance. In order to evaluate the closure you have to call it with parameters, i.e. something like >> makeSomething(something, "dummy") would work. But this way you have to repeat the first getByValue parameter and make up a dummy for the second, unspecified one because you have no easy way to refer to it other than introducing yet another closure like >> { Something smth, String value -> makeSomething(smth, value) }. But then you are not saving a lot of code.

It is your decision if this is nicer than somethingCall(2, Something.TWO) (I like it, actually) or if you go for my contrived construct. What I cannot do for you is change Groovy or Spock DSL syntax just because you prefer it to look different.

kriegaex
  • 63,017
  • 15
  • 111
  • 202