如何使用 RequestFixture 在 Ratpack 中模拟会话?

How do I mock a Session in Ratpack with RequestFixture?

我想做的是测试身份验证处理程序,但我的问题归结为注册表中没有 Session 实例。

示例测试:

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
    }

}

(额外的重新抛出是为了让我们看到在 ratpack 测试中抛出的异常,因为默认情况下它会被捕获并隐藏在结果中。)

我知道原则上我可以创建一个 Session 实例并使用 registry { add <Session instance> } 块将其添加到注册表中,但我已经深入研究了 Ratpack 代码,并且创建一个 Session 对象需要很多不同的其他组件并将它们传递给 SessionModule#sessionAdaptor(或 DefaultSession 构造函数)。我找不到任何这样做的例子,看来这个调用是由我无法取消选择的 Guice 依赖注入魔法处理的。

在应用程序中执行此操作的常用方法是使用 bind { module SessionModule } 块,但这无法从 RequestFixture#execute.

的上下文中访问

由于会话对于任何 Web 应用程序来说都是面包和黄油,我的直觉是这可能是一个容易解决的问题,我只是没有找到正确的方法?

您可以通过 GroovyRequestFixture.handle(handler, closure) 方法调用访问 Registry,例如,您可以注册模拟 Session 对象:

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

看看下面的例子:

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']

    }
}

在这个测试中,我为键 test 模拟了 Session 对象来存储 Lorem ipsum 文本。当运行这个测试时,两个断言都通过了。

替代方法:注册 Guice.registry()

如果您不想使用模拟的 Session 对象,您可以尝试用 Guice 注册表对象替换默认 Ratpack 的 Registry。首先,初始化一个创建 Guice 注册表的函数,并通过绑定添加 SessionModule

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

接下来在 GroovyChainActionexecute() 方法中,您可以通过调用以下方法替换默认注册表:

register(guiceRegistry.apply(registry))

不再有模拟,但在这种情况下,您无法访问请求范围外的 Session 对象,因此您将无法在测试准备阶段向会话添加任何内容。您可以在下面找到完整的示例:

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]

    }
}

希望对您有所帮助。