java - 是否可以使用 Spock stub 或模拟 SocketChannel?

标签 java unit-testing spock socketchannel

本质上,我有一个 Java 类,它在套接字 channel 上执行选择,并且我想对 channel 进行 stub ,以便我可以测试选择是否按预期工作。

例如,被测试的类大致是这样做的:

class TestedClass {
    TestedClass(SocketChannel socket) { this.socket = socket }

    // ...
    SocketChannel socket;
    // ...
    void foo() {
        // Wait a while for far end to close as it will report an error 
        // if we do it.  But don't wait forever! 
        // A -1 read means the socket was closed by the other end.
        // If we select on a read, we can check that, or time out 
        // after a reasonable delay.

        Selector selector = Selector.open();
        socket.configureBlocking(false);
        socket.register(selector, SelectionKey.OP_READ);
        while(selector.select(1000) == 0) {
            Log.debug("waiting for far end to close socket...")
        }

        ByteBuffer buffer = ByteBuffer.allocate(1);
        if (socket.read(buffer) >= 0) {
            Log.debug("far end didn't close");
            // The far end didn't close the connection, as we hoped
            abort(AbortCause.ServerClosed);
        }

        Log.debug("far end closed");
    }
}

我希望能够测试这样的东西:

def "test we don't shut prematurely" () {
    when:
    boolean wasClosedPrematurely
    SocketChannel socket = Stub(@SocketChannel) {
        // insert stub methods here ....
    }

    TestedClass tc = new TestedClass(socket)
    tc.foo();

    then:
    wasClosedPrematurely == false
}

这基于一个真实的例子,但细节并不重要。总体目标是如何对支持选择的 SocketChannels 进行 stub ,这样我就不必创建一个真实的客户端来进行测试。

我也知道它比仅仅 stub SocketChannel 更复杂:似乎我需要拦截 Selector.open() 或以某种方式提供自定义系统默认 SelectorProvider。如果我只是 stub SocketChannel,当我尝试使用我的 stub 注册通过 Selection.open() 获得的选择器时,我会得到一个 IllegalSelectorException,不幸的是,基本 AbstractSelectableChannel#register 方法是最终。

但是我找不到任何有用的指示来说明如何或是否可以使用 Spock Mocks 实现这一点,而且这似乎是一个很常见的事情,所以在这里问一个很好的问题。有人可以帮忙吗?

最佳答案

Spock 使用 CGLIB模拟/ stub / spy 类。 CGLIB 无法覆盖 final方法。 SocketChannel有很多final方法(例如configureBlocking),但是CGLIB不会失败而是使用原始方法。由于 configureBlocking 是最终的,因此它会在您的测试中使用。

公共(public)最终 SelectableChannel configureBlocking( boolean block ) 抛出 IOException { 同步(regLock){ if (!isOpen()) 抛出新的 ClosedChannelException(); if(阻塞==阻塞) 返回这个; if ( block && hasValidKeys()) 抛出新的 IllegalBlockingModeException(); implConfigureBlocking( block ); 阻塞=阻塞; } 返回这个; }

因此,configureBlocking 需要初始化 regLock 变量,但是当您为此类创建 stub 时,该变量未初始化,并且您会在此处得到 NPE。

问题是如何处理它? 好吧,我想说,首先,尝试使用接口(interface)而不是类。 如果不可能,请尝试不要调用 final方法。 如果仍然不可能,您必须查看类(class)内部并找出应该 mock 的内容。 我看到的最后一个选项是进行完整的集成测试:创建两个套接字并连接它们。

关于java - 是否可以使用 Spock stub 或模拟 SocketChannel?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41185351/

相关文章:

java - Clojure:通过代理或其他方式访问 protected 方法?

python - 恢复模式下的 etree.XMLParser 是否仍会抛出解析错误?

c# - EF6 模拟派生 DbSet

使用 robolectric 进行 Android gradle 测试

java - hibernate 获取非默认 mysql 端口的拒绝连接

java - 如何在jsp中制作漂亮的表格

java - Spring boot 2.0.X属性异常处理程序响应ascii?

testing - Grails/Spock 如何测试特定约束是否失败?

eclipse - Eclipse 中junit 的等宽字体已弃用

unit-testing - 在Spock单元测试中对抛出的异常进行错误测试