繁体   English   中英

测试Java套接字

[英]Testing Java Sockets

我正在开发一个网络应用程序,我希望得到正确的单元测试。 这个时候我们会这样做,你知道吗? :)

不过,我在测试网络连接时遇到了麻烦。

在我的应用程序中,我使用普通的java.net.Socket

例如:

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Message {
    byte[] payload;

    public Message(byte[] payload) {
        this.payload = payload;
    }

    public boolean sendTo(String hostname, int port) {
        boolean sent = false;

        try {
            Socket socket = new Socket(hostname, port);

            OutputStream out = socket.getOutputStream();

            out.write(payload);

            socket.close();

            sent = true;
        } catch (UnknownHostException e) {
        } catch (IOException e) {
        }

        return sent;
    }
}

我读到了关于嘲笑但不确定如何应用它。

如果我要测试代码,我会做以下事情。

首先,重构代码,以便Socket不会在您要测试的方法中直接实例化。 下面的例子显示了我能想到的最小变化。 未来的更改可能会将Socket创建分解为一个完全独立的类,但我喜欢小步骤,我不喜欢对未经测试的代码进行大的更改。

public boolean sendTo(String hostname, int port) {
    boolean sent = false;

    try {
        Socket socket = createSocket();
        OutputStream out = socket.getOutputStream();
        out.write(payload);
        socket.close();
        sent = true;
    } catch (UnknownHostException e) {
        // TODO
    } catch (IOException e) {
        // TODO
    }

    return sent;
}

protected Socket createSocket() {
    return new Socket();
}

现在套接字创建逻辑不在您尝试测试的方法之外,您可以开始模拟并挂钩创建套接字。

public class MessageTest {
    @Test
    public void testSimplePayload() () {
        byte[] emptyPayload = new byte[1001];

        // Using Mockito
        final Socket socket = mock(Socket.class);
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        when(socket.getOutputStream()).thenReturn(byteArrayOutputStream);

        Message text = new Message(emptyPayload) {
            @Override
            protected Socket createSocket() {
                return socket;
            }
        };

        Assert.assertTrue("Message sent successfully", text.sendTo("localhost", "1234"));
        Assert.assertEquals("whatever you wanted to send".getBytes(), byteArrayOutputStream.toByteArray());
    }
}

在要测试的单元上覆盖单个方法对于测试非常有用,尤其是在具有可怕依赖性的丑陋代码中。 显然,最好的解决方案是整理出依赖关系(在这种情况下,我认为Message不依赖于Socket ,也许有一个Messager接口就像glowcoder建议的那样),但是在尽可能小的步骤中向解决方案迈进是很好的。

我将按照要求回答您的问题,而不是重新设计您的课程(其他人已经涵盖了这些问题,但是关于课程的基本问题仍然有效)。

单元测试从不测试被测试类之外的任何东西。 这对我的大脑造成了一段时间的伤害 - 这意味着单元测试不会以任何方式证明你的代码有效! 它的作用是证明您的代码与编写测试时的代码相同。

所以说你想要这个类的单元测试,但你也想要一个功能测试。

对于单元测试,您必须能够“模拟”通信。 要做到这一点而不是创建自己的套接字,从“套接字工厂”中获取一个,然后自己做一个套接字工厂。 应该将工厂传递给您正在测试的此类的构造函数。 这实际上并不是一个糟糕的设计策略 - 您可以在工厂中设置主机名和端口,这样您就不必在通信类中了解它们 - 更抽象。

现在在测试中你只是传入一个模拟工厂,创建模拟套接字,一切都是玫瑰。

不要忘记功能测试! 设置一个可以连接的“测试服务器”,将一些消息发送到服务器并测试您收到的响应。

就此而言,您可能希望进行更深入的功能测试,其中您编写一个客户端,向REAL服务器发送一些脚本命令并测试结果。 您甚至可能只想为功能测试创建“重置状态”命令。 功能测试实际上确保整个“功能单元”按预期一起工作 - 这是许多单元测试倡导者忘记的事情。

我不会说这是个坏主意。

我要说它可以改进。

如果您通过套接字发送原始字节[],则另一方可以使用它执行任何操作。 现在,如果您没有连接到Java服务器,那么您可能需要这样做。 如果您愿意说“我一直在使用Java服务器”,那么您可以使用序列化来获得优势。

执行此操作时,您可以通过创建自己的Sendable对象来模拟它,就好像它们遇到了线路一样。

创建一个套接字,而不是每个消息一个。

interface Sendable {

    void callback(Engine engine);

}

那么这在实践中如何运作?

信使类:

/**
 * Class is not thread-safe. Synchronization left as exercise to the reader
 */
class Messenger { // on the client side

    Socket socket;
    ObjectOutputStream out;

    Messenger(String host, String port) {
        socket = new Socket(host,port);
        out = new ObjectOutputStream(socket.getOutputStream());
    }

    void sendMessage(Sendable message) {
        out.writeObject(message);
    }

}

接收者类:

class Receiver extends Thread { // on the server side

    Socket socket;
    ObjectInputStream in;
    Engine engine; // whatever does your logical data

    Receiver(Socket socket, Engine engine) { // presumable from new Receiver(serverSocket.accept());
        this.socket = socket;
        this.in = new ObjectInputStream(socket.getInputStream());
    }

    @Override
    public void run() {
        while(true) {
            // we know only Sendables ever come across the wire
            Sendable message = in.readObject();
            message.callback(engine); // message class has behavior for the engine
        }
    }
}

测试连接和服务器交互很困难。

简而言之,我将业务逻辑与通信逻辑隔离开来。

我为业务逻辑创建单元测试场景,这些测试是自动的(使用JUni,t和Maven),我创建其他场景来测试实际连接,我不使用像JUnit这样的框架进行这些测试。

在去年我使用Spring Mocks用HttpResponse HttpRequest测试逻辑,但我认为这没用。

我关注这个问题。

再见

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM