1

How do we unit test logic in Promises.task?

task{service.method()} 

I want to validate invocation of the service method inside the task.

Is this possible? If yes, how?

I read in the documentation that in unit testing async processes, one can use this:

Promises.promiseFactory = new SynchronousPromiseFactory()

Tried adding it in my setup, but still does not work.

froi
  • 7,268
  • 5
  • 40
  • 78

4 Answers4

3

The long way

I've been struggling with this for a moment too.

I tried those:

  1. grails unit test + Thread
  2. Verify Spock mock with specified timeout

Also tried the same solution from the docs as you:

Promises.promiseFactory = new SynchronousPromiseFactory()

All went with no luck.

The solution

So I ended up with meta classing.

In the test's setup method, I mocked the Promise.task closure, so it runs the closure in the current thread, not in a new one:

def setup() {
    Promises.metaClass.static.task = { Closure c -> c() }
    // ...more stuff if needed...
}

Thanks to that, I can test the code as it wouldn't use multi threading.

Even I'm far from being 100% happy with this, I couldn't get anything better so far.

Marcin Świerczyński
  • 2,302
  • 22
  • 32
1

In recent versions of Grails (3.2.3 for instance), there is no need to mock, metaClass or use a Promise factory. I found out the promises in unit tests get executed synchronously. Found no doc for that, I empirically added a sleep inside a promise and noticed the test waited for the pause to complete.

For integration tests and functional tests, that's another story: you have to change the promise provider, for instance in BootStrap.groovy:

if (Environment.current == Environment.TEST) {
    Promises.promiseFactory = new SynchronousPromiseFactory()
}

Like Marcin suggested, the metaClass option is not satisfactory. Also bear in mind that previous (or future) versions of Grails are likely to work differently.

youri
  • 3,685
  • 5
  • 23
  • 43
  • Using `Promises.promiseFactory = new SynchronousPromiseFactory()` works great for me in my controller test. Thanks. – Zorobay Feb 03 '22 at 14:17
1

If you are stuck with Grails 2 like dinosaurs such as me, then you can just copy the classes SynchronousPromiseFactory and SynchronousPromise from Grails 3 to your project and then the following works: Promises.promiseFactory = new Grails3SynchronousPromiseFactory() (Class names are prefixed with Grails3 to make the hack more obvious)

0

I'd simply mock/override the Promises.task method to invoke the provided closure directly.

defectus
  • 1,947
  • 2
  • 16
  • 21