Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • 06:50
    Net-burst commented #1240
  • 01:23
    kriegaex commented #1240
  • 00:39
    kriegaex commented #1240
  • 00:39
    kriegaex commented #1240
  • 00:38
    kriegaex commented #1240
  • 00:37
    kriegaex commented #1240
  • Nov 27 14:36
    Net-burst commented #1240
  • Nov 27 12:30
    kriegaex commented #1240
  • Nov 27 12:17
    rtretyak commented #1232
  • Nov 26 21:29
    Net-burst opened #1240
  • Nov 26 17:44

    spockbot on gh-pages

    Publish docs/2.0-SNAPSHOT (compare)

  • Nov 26 17:27
    codecov[bot] commented #1239
  • Nov 26 17:26
    codecov[bot] commented #1239
  • Nov 26 17:26
    codecov[bot] commented #1239
  • Nov 26 17:25
    codecov[bot] commented #1239
  • Nov 26 17:25
    codecov[bot] commented #1239
  • Nov 26 17:19
    leonard84 review_requested #1239
  • Nov 26 17:19
    leonard84 opened #1239
  • Nov 26 16:33
    Vampire commented #1238
  • Nov 26 16:27
    leonard84 commented #1238
Artjom Kalita
@artjomka
Iagree type safe variant looks better and it works great
Björn Kautler
@Vampire
I don't think it did.
Did you follow my advice, using a version where you think it works and change the production code to provoke a test failure?
Artjom Kalita
@artjomka
Usually doing that stuff , but some people dont and with big codebase stuff happens
Björn Kautler
@Vampire

In

1 * transactionRepository.saveAll({
    [{
        it.paymentDistribution == paymentDistribution
        it.amount == transactionAmountExpected
        it.type == transactionTypeExpected
    }]
} as List<Transaction>)

the argument matcher is

{
    [{
        it.paymentDistribution == paymentDistribution
        it.amount == transactionAmountExpected
        it.type == transactionTypeExpected
    }]
} as List<Transaction>

which is a combination of a code constraint with a type constraint,
so this checks that a List<Transaction> is given and that the given code constraint is evaluating to true.
The code constraint is

{
    [{
        it.paymentDistribution == paymentDistribution
        it.amount == transactionAmountExpected
        it.type == transactionTypeExpected
    }]
}

which is exactly one expression.
The expression is a list of closures with one member.
This expression always evaluates to true as the Groovy truth for a list is, that it is not null and not empty.

In older Spock versions this compiled find and it will compile fine in 2.0-M4 which you can try as 2.0-SNAPSHOT already since yesterday.
But it will never test what you exepct I think but always be green.
That's why I suggested you try to make the test fail and said "never trust a test you didn't have seen to fail for the intended reason"
Björn Kautler
@Vampire

In Alexanders suggestion

1 * transactionRepository.saveAll({ List<Transaction> transactions ->
    transactions.size() == 1
    transactions[0].paymentDistribution == paymentDistribution
    transactions[0].amount == transactionAmountExpected
    transactions[0].type == transactionTypeExpected
})

or the less type-safe but more Groovy version

1 * transactionRepository.saveAll({
    it.size() == 1
    with (it.first()) {
        paymentDistribution == expectedPaymentDistribution
        amount == expectedTransactionAmountExpected
        type == expectedTransactionTypeExpected
    }
})

there are 4 expressions in the code constraint that do the checks you (or your colleague) intended

Artjom Kalita
@artjomka
Correct me if I am wrong but default version in test was just checking that it is List but without going into deeper details comparing parameters ?
Björn Kautler
@Vampire
If you mean with "default version" the one you posted above, then yes.
If I'm not mistaken it only checked that is was a List<Transaction> as the code constraint always evaluated to true.
That's why I suggested you try to break the test.
Artjom Kalita
@artjomka
okay let me rollback and check it :)
Björn Kautler
@Vampire
Btw. if there is not only .saveAll(List<Transaction>) but also other .saveAll methods, the as List<Transaction> should of course also be included like
1 * transactionRepository.saveAll({
    it.size() == 1
    with (it.first()) {
        paymentDistribution == expectedPaymentDistribution
        amount == expectedTransactionAmountExpected
        type == expectedTransactionTypeExpected
    }
} as List<Transaction>)
Artjom Kalita
@artjomka
This variant is not working with 1.1 spock version - it is failing with
Too few invocations for:

1 * transactionRepository.saveAll({
                it.size() == 1
                with (it.first()) {
                    paymentDistribution == paymentDistribution
                    amount == transactionAmountExpected
                    type == transactionTypeExpected
                }
            })   (0 invocations)
but with 1.3 it is working properly
Björn Kautler
@Vampire

Yeah, could be.
In 1.1 and 1.2 the code constraints where not yet implicit assertion blocks but need to return a boolean, that was added in 1.3.
So in 1.1. and 1.2 it would probably be something like

1 * transactionRepository.saveAll({
    it.size() == 1 &&
    it.first().paymentDistribution == expectedPaymentDistribution &&
    it.first().amount == expectedTransactionAmountExpected &&
    it.first().type == expectedTransactionTypeExpected
} as List<Transaction>)

Besides that, in your shown output paymentDistribution == paymentDistribution will always be true ;-)

Björn Kautler
@Vampire
https://bit.ly/2020-groovy-ecosystem-usage-report show your Spock usage with this one-question survey :-)
mlasevich
@mlasevich
when using a data-driven tests, is there a nice way to enter larger/more complicated values in the "where" block? the lines get long sometimes
Leonard Brünings
@leonard84
well it depends on the nature of your data
mlasevich
@mlasevich
in most basic case, testing a nested map parser, so various deep nested maps
Leonard Brünings
@leonard84
you could extract it as json and then just parse that those files in the where block
mlasevich
@mlasevich
e.g. as one of the column values in data:
[
      source : 'config',
      key1   : 'config1',
      map2   : [
          map2key2: 'map2key2value2'
      ],
      map1   : [
          map1key1: 'map1key1value2'
      ],
      list1  : ['list1item1'],
      notnull: "",
      empty  : ""
  ]
hmm, that can be interesting thought
mlasevich
@mlasevich

@leonard84 to be clear, are you suggesting something like:

given: 
def jsonSlurper = new JsonSlurper()
def testData = jsonSlurper.parseText '''
   [ my test data ]
...
where:
 value1                           | value2                          || result
  testData[0].value1  | testData[0].value2  || result

or is there a better way?

Leonard Brünings
@leonard84
Well I would extract those into files and just pass the file name as parameters
mlasevich
@mlasevich
I am clearly a Spock noob, can you point me at an example of using external files in this case?
Leonard Brünings
@leonard84
well jsonSlurper.parse(new File(filename))
mlasevich
@mlasevich
Oh, i though you could point "where" to a file
Leonard Brünings
@leonard84
no I'd put the filenames in where
mlasevich
@mlasevich
is there a way to hand where an array or something so that I can define all my tests in one JSON block?
Leonard Brünings
@leonard84
sure
and the chapter below that
mlasevich
@mlasevich
awesome... ~to be clear, whatever is on the other side (in example sql.rows()) must return an array of arrays, with each of the inner arrays same length as the number of params?~ (all is there, I will read, thank you)
(thank you so much!)
Leonard Brünings
@leonard84
well it must return an Iterable that contains either Lists or arrays IIRC
it is basically https://groovy-lang.org/semantics.html#_multiple_assignment with a slightly different syntax
mlasevich
@mlasevich
ok, will figure it out
Thank you
spartanhooah
@spartanhooah

I'm working on cleaning up some tests. They involved a stubbed Spring RestTemplate. It is set up like this:

def statusCode = HttpStatus.OK
RestTemplate restTemplate = Stub()
ResponseEntity<String> response = Stub()

setup() {
    response.getStatusCode() >> { statusCode }
    response.getBody() >> "body"
}

Most of the tests are happy path, and so the restTemplate stub is defined like this:

given:
restTemplate.exchange(*_) >> response

However, some tests are sad path and the stub is defined like this:

given:
restTemplate.exchange(*_) >> { throw new HttpClientErrorException(HttpStatus.BAD_REQUEST) }

Is there a way to have the common case in setup() so I don't have to repeat it so often?

Leonard Brünings
@leonard84
yes
class OverrideStubbing extends Specification{
  List stub = Stub() {
    get(_) >> 1
  }

  def "normal"() {
    expect:
    stub.get(0) == 1
  }

  def "override"() {
    when:
    def result = stub.get(0)

    then:
    stub.get(_) >> 42
    result == 42
  }
}
I personally advise against mocking things like RestTemplate, as these things can have complex interactions that are not covered by simple unit tests. In my projects I abstract the RestTemplate in a FooClient interface+class that can be mocked, and then test the FooClient itself against MockServer/WireMock so that the stuff like parsing/contentType handling and so on is properly tested.
spartanhooah
@spartanhooah
Oh, right, it's got to go in the then block.
Björn Kautler
@Vampire
This message was deleted
mlasevich
@mlasevich
this may be wrong place to ask, but is there a way to get code coverage for spock when script was loaded via GroovyScriptEngine.loadScriptByName() ?
Björn Kautler
@Vampire
That's probably more a question for JaCoCo and Groovy, it is not really related to Spock
mlasevich
@mlasevich
yeah, it was a long shot - but I assume people use Spock to test scripts somehow....
I think I found a workaround though
Leonard Brünings
@leonard84
@mlasevich anything worth sharing?
mlasevich
@mlasevich
Basically if you load it via Script, it will not work, so I made a copy of the class in the classpath, and then have my script extend the classpath copy with no body. Hacky AF, but gets the intended results
I imagine there is a way to game it by adding actual script to classpath and creating a fake script to load