How do I mock a Session in Ratpack with RequestFixture?

旧城冷巷雨未停 提交于 2019-12-24 12:09:50

问题


What I'm trying to do is test an authentication handler, but my problem boils down to having no Session instance in the registry.

An example test:

package whatever

import groovy.transform.CompileStatic
import ratpack.groovy.handling.GroovyChainAction
import ratpack.groovy.test.handling.GroovyRequestFixture
import ratpack.http.Status
import ratpack.session.Session
import spock.lang.Specification

class SessionChainTest extends Specification {
    GroovyChainAction sessionChain = new GroovyChainAction() {
        @Override
        @CompileStatic
        void execute() throws Exception {
            get('foo') {
                Session s = get Session
                // Stuff using session here
            }
        }
    }

    def "should get session"() {
        given:
        def result = GroovyRequestFixture.handle(sessionChain) {
            uri 'foo'
            method 'GET'
        }

        expect:
        result.status == Status.OK

        // If the server threw, rethrow that
        Throwable t = result.exception(Throwable)
        if (t) throw t // <<< Throws NotInRegistryException because no Session in registry
    }

}

(The extra rethrow is in there to allow us to see the exception thrown within the ratpack test, because by default it is caught and stashed in the result.)

I know that in principle I could create a Session instance and add it to the registry with a registry { add <Session instance> } block, but I've delved into the Ratpack code, and creating a Session object requires getting a lot of disparate other components and passing them to SessionModule#sessionAdaptor (or the DefaultSession constructor). I can't find any examples of that being done, it appears this call is handled by Guice dependency-injection magic I can't unpick.

The usual way to do it in an application is to use a bind { module SessionModule } block but this isn't accessible from the context of RequestFixture#execute.

As sessions are bread and butter for any web application, my hunch is that this may be an easily solved problem, I just haven't found the right way to do it?


回答1:


You can access Registry through GroovyRequestFixture.handle(handler, closure) method call and you can e.g. register mocked Session object:

GroovyRequestFixture.handle(sessionChain) {
    uri 'foo'
    method 'GET'
    registry { r ->
        r.add(Session, session)
    }
}

Take a look at following example:

import groovy.transform.CompileStatic
import ratpack.exec.Promise
import ratpack.groovy.handling.GroovyChainAction
import ratpack.groovy.test.handling.GroovyRequestFixture
import ratpack.http.Status
import ratpack.jackson.internal.DefaultJsonRender
import ratpack.session.Session
import spock.lang.Specification

import static ratpack.jackson.Jackson.json

class SessionChainTest extends Specification {

    Session session = Mock(Session) {
        get('test') >> Promise.value(Optional.of('Lorem ipsum'))
    }

    GroovyChainAction sessionChain = new GroovyChainAction() {
        @Override
        @CompileStatic
        void execute() throws Exception {
            get('foo') {
                Session s = get Session

                s.get('test').map { Optional<String> o ->
                    o.orElse(null)
                }.flatMap { value ->
                    Promise.value(value)
                }.then {
                    render(json([message: it]))
                }
            }
        }
    }

    def "should get session"() {
        given:
        def result = GroovyRequestFixture.handle(sessionChain) {
            uri 'foo'
            method 'GET'
            registry { r ->
                r.add(Session, session)
            }
        }

        expect:
        result.status == Status.OK

        and:
        result.rendered(DefaultJsonRender).object == [message: 'Lorem ipsum']

    }
}

In this test I mock Session object for key test to store Lorem ipsum text. When running this test, both assertions pass.

Alternative approach: registering Guice.registry()

If you don't want to use mocked Session object you can try replacing default Ratpack's Registry with a Guice registry object. Firstly, initialize a function that creates Guice registry and add SessionModule via bindings:

static Function<Registry, Registry> guiceRegistry = Guice.registry { bindings ->
    bindings.module(new SessionModule())
}

Next inside execute() method of GroovyChainAction you can replace the default registry by calling:

register(guiceRegistry.apply(registry))

No mocks anymore, but in this case you can't access Session object outside request scope, so you wont be able to add anything to the session in preparation stage of your test. Below you can find full example:

import groovy.transform.CompileStatic
import ratpack.exec.Promise
import ratpack.func.Function
import ratpack.groovy.handling.GroovyChainAction
import ratpack.groovy.test.handling.GroovyRequestFixture
import ratpack.guice.Guice
import ratpack.http.Status
import ratpack.jackson.internal.DefaultJsonRender
import ratpack.registry.Registry
import ratpack.session.Session
import ratpack.session.SessionModule
import spock.lang.Specification

import static ratpack.jackson.Jackson.json

class SessionChainTest extends Specification {

    static Function<Registry, Registry> guiceRegistry = Guice.registry { bindings ->
        bindings.module(new SessionModule())
    }

    GroovyChainAction sessionChain = new GroovyChainAction() {
        @Override
        @CompileStatic
        void execute() throws Exception {
            register(guiceRegistry.apply(registry))

            get('foo') {
                Session s = get Session

                s.get('test').map { Optional<String> o ->
                    o.orElse(null)
                }.flatMap { value ->
                    Promise.value(value)
                }.then {
                    render(json([message: it]))
                }
            }
        }
    }

    def "should get session"() {
        given:
        def result = GroovyRequestFixture.handle(sessionChain) {
            uri 'foo'
            method 'GET'
        }

        expect:
        result.status == Status.OK

        and:
        result.rendered(DefaultJsonRender).object == [message: null]

    }
}

Hope it helps.



来源:https://stackoverflow.com/questions/50814817/how-do-i-mock-a-session-in-ratpack-with-requestfixture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!