测试 Java 套接字

2022-09-01 07:24:10

我正在开发一个网络应用程序,我想进行正确的单元测试。这一次我们会这样做,你知道吗?:)

但是,我在测试网络连接时遇到问题。

在我的应用程序中,我使用普通s。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;
    }
}

我读过关于嘲笑的文章,但不知道如何应用它。


答案 1

如果我要测试代码,我会执行以下操作。

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

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());
    }
}

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


答案 2

我将按照问题回答你的问题,而不是重新设计你的类(其他人已经涵盖了这一点,但是关于所写的类的基本问题是有效的)。

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

也就是说,你想要一个单元测试来测试这个类,但你也想要一个功能测试。

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

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

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

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


推荐