Unit testing chains

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

Unit testing chains

Marek Piechut
Is there any way to easily unit test chains (just like there's a UnitTest class for Handlers)?

I was looking around but couldn't find any easy way to do this without mocking LaunchConfig and executing "execute" method manually.

By the way, I'm trying to split my application into separate chains and join them together in main chain. Is this a good idea? Or should I rather use something else?

Ex:

prefix('disciplines', chain(get(SomeChain)))

class SomeChain implements Action<Chain> {
    @Override
    void execute(Chain chain) throws Exception {
        chain.with {
            get { context ->
                context.with {
                    render "List"
                }
            }

            get(':single') { context ->
                context.with {
                    render "show ${pathTokens.single}"
                }
            }
        }
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Luke Daley
Administrator


> Is there any way to easily unit test chains (just like there's a UnitTest class for Handlers)?

A chain is really just a handler.

def result = UnitTest.invoke(Handlers.chain(null, new SomeChain()) {
  …
}

`null` is the launch config there, which you can null out unless your handlers try and access it.

> I was looking around but couldn't find any easy way to do this without mocking LaunchConfig and executing "execute" method manually.

Yeah, you don't want to do this.

>  By the way, I'm trying to split my application into separate chains and join them together in main chain. Is this a good idea? Or should I rather use something else?

That's a perfectly fine idea.

> Ex:
>
> prefix('disciplines', chain(get(SomeChain)))
>
> class SomeChain implements Action<Chain> {
>     @Override
>     void execute(Chain chain) throws Exception {
>         chain.with {
>             get { context ->
>                 context.with {
>                     render "List"
>                 }
>             }
>
>             get(':single') { context ->
>                 context.with {
>                     render "show ${pathTokens.single}"
>                 }
>             }
>         }
>     }
> }

I've just made some changes to make this a bit easier…

import ratpack.groovy.Groovy

class SomeChain implements Action<Chain> {
    @Override
    void execute(Chain chain) throws Exception {
        Groovy.chain(chain) {
            get {
              render "List"
            }

            get(':single') {
              render "show ${pathTokens.single}"
            }
        }
    }
}


--

Luke Daley.

Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Marek Piechut
Hmm...
Tried it out and got NPE:

java.lang.NullPointerException
        at ratpack.handling.internal.ChainBuilders.build(ChainBuilders.java:36)
        at ratpack.handling.Handlers.chain(Handlers.java:96)
        at ratpack.handling.Handlers.chain(Handlers.java:83)
        at test.ChainTest.should properly list available disciplines(ChainTest.groovy:18)
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Luke Daley
Administrator
This is fixed in the latest 0.9.2-SNAPSHOT.

If you want to use an earlier version, you'll need to supply a mock for LaunchConfig that returns false for isReloadable.
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Marek Piechut
That worked, thanks.

I was using 0.9.1. I'm not that "bleeding edge" :).

Thanks.
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Marek Piechut
In reply to this post by Marek Piechut
Hi.

It looks like your change simplifying chaining in Groovy didn't go to 0.9.2.
You reverted it in commit 3e69ce6a066e6b95b8428ff893a580e456b075b2

I mean this code in Groovy class:

public static void chain(Chain chain, @DelegatesTo(value = GroovyChain.class, strategy = Closure.DELEGATE_FIRST) Closure<?> closure) throws Exception {
    GroovyChain groovyChain = chain(chain);
    new ClosureInvoker<Object, GroovyChain>(closure).invoke(chain.getRegistry(), groovyChain, Closure.DELEGATE_FIRST);
}

Any chance something like this comes in next releases? It would really make my code nicer to read.
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Luke Daley
Administrator
Hi,

I introduced MockLaunchConfig to make unit testing easier: http://www.ratpack.io/manual/0.9.3/api/ratpack/test/MockLaunchConfig.html

You can just pass `new MockLaunchConfig()` as the launch config instance.

Does this work for you?
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Marek Piechut
Hi.

I meant Groovy.chain(Chain, Closure) method. :)
It makes code in Action<Chain> classes easier to read and write:

Groovy.chain(chain) { 
    get { 
        render "List" 
    } 
}

Instead of:

void execute(Chain chain) throws Exception { 
     chain.with { 
         get { context -> ...
    }
}
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Luke Daley
Administrator
Now I understand.

I've added this back for 0.9.3 (released in about 24 hours): https://github.com/ratpack/ratpack/commit/5b95d9d50f01a3d5d8db6b1ab23d15ea8fbfeed3

You might also be interested in the new GroovyChainAction class: http://www.ratpack.io/manual/0.9.3/api/ratpack/groovy/handling/GroovyChainAction.html
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Marek Piechut
GroovyChainAction looks very nice.

Thanks.
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Minjae Lee
In reply to this post by Luke Daley
Is MockLaunchConfig deprecated since 0.9.4 version?
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Luke Daley
Administrator
Yes it is. What are you trying to use it for?
Reply | Threaded
Open this post in threaded view
|

Re: Unit testing chains

Minjae Lee
Okay that's fine.
I was actually going to use it for testing, but did it by using RequestFixture.
Thank you though.