Home > Net >  Writing Spock test to verify method invocation in async method
Writing Spock test to verify method invocation in async method

Time:07-23

If I have a standard method like this:

private MessageReceiver getMessageReceiver() {
    method1();
}

In Spock, I can test the number of calls like this:

1 * method1(); 

But in my case the method looks like this (used to received pubsub messages)

private MessageReceiver getMessageReceiver() {
    return (message, consumer) -> {
        method1();
    }
}

How can I write a Spock test to do the same as before and verify that method1 was called one time?

CodePudding user response:

I agree with Leonard that it is your job to provide an MCVE. Today is your lucky day, however, and I had some time to play around and speculate about your situation, which I think is as follows:

interface MessageReceiver {
  def process(def message, def consumer)
}
class UnderTest {
  def doSomething() {
    Thread.start { getMessageReceiver().process("message", "consumer") }
  }

  private MessageReceiver getMessageReceiver() {
    return { message, consumer ->
      method1()
    }
  }

  def method1() {
    sleep 500
    println "Doing something"
  }
}

Please note that I am using a Groovy closure here instead of a Java lambda in order to make the code compatible with Groovy versions < 4.

Spock has built-in support for testing asynchronous code. Unfortunately, the Spock manual does not cover AsyncConditions, BlockingVariable and BlockingVariables at all and only mentions PollingConditions as part of the old 0.7 release notes from 2012 in passing. You need to experiment with those classes or check Spock's internal tests in order to find out more about them. In this case, I am showing you a polling conditions variant:

package de.scrum_master.stackoverflow.q72958882

import spock.lang.Specification
import spock.util.concurrent.PollingConditions

class AsyncMethodTest extends Specification {
  def "my feature"() {
    given:
    int timesExecuted
    // In order to verify interactions on self-invoked methods of a class under
    // test, we need a spy.
    UnderTest underTest = Spy() {
      method1() >> {
        // On the one hand, we need to stub this method in order to increment a
        // counter which can then be checked in the polling condition. On the
        // other hand, we  need to call the real method in order to make the
        // class under test behave normally.
        callRealMethod()
        timesExecuted  
      }
    }

    when:
    underTest.doSomething()

    then:
    new PollingConditions(timeout: 1).eventually {
      timesExecuted == 1
    }
  }
}

Try it in the Groovy Web Console.

Update: Of course, you can omit callRealMethod(), if the real method is very slow and you want to speed up the test, using a very short polling condition.

  • Related