You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java串口单元测试:如何虚拟串口替代硬件传感器?

当然可行!而且有几种靠谱的方案能帮你在没有硬件传感器的情况下,顺利测试串口通信逻辑。下面我给你拆解下具体思路和实现方式:

方案一:依赖注入+自定义串口适配器(最推荐)

因为jssc的SerialPort是具体类而非接口,直接模拟会有局限。我们可以通过适配器模式给串口操作套一层接口,让业务逻辑和具体的串口实现解耦,测试时替换成模拟实现即可。

步骤1:定义串口操作接口

先抽象出你需要的串口方法,比如:

public interface SerialPortHandler {
    boolean open() throws SerialPortException;
    void close() throws SerialPortException;
    byte[] read(int byteCount) throws SerialPortException;
    void write(byte[] data) throws SerialPortException;
    // 按需添加其他方法,比如设置波特率、注册数据监听等
}

步骤2:生产环境的JSSC实现

基于jssc写实际运行时的适配器:

public class JsscSerialPortHandler implements SerialPortHandler {
    private final SerialPort serialPort;

    public JsscSerialPortHandler(String portName) {
        this.serialPort = new SerialPort(portName);
    }

    @Override
    public boolean open() throws SerialPortException {
        return serialPort.openPort();
    }

    @Override
    public void close() throws SerialPortException {
        serialPort.closePort();
    }

    @Override
    public byte[] read(int byteCount) throws SerialPortException {
        return serialPort.readBytes(byteCount);
    }

    @Override
    public void write(byte[] data) throws SerialPortException {
        serialPort.writeBytes(data);
    }
}

步骤3:测试用的模拟适配器

写一个完全可控的模拟类,用来模拟传感器的行为:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Arrays;

public class MockSerialPortHandler implements SerialPortHandler {
    private boolean isOpen = false;
    private final Queue<byte[]> incomingData = new LinkedList<>();
    private final List<byte[]> outgoingData = new ArrayList<>();

    @Override
    public boolean open() {
        isOpen = true;
        return true;
    }

    @Override
    public void close() {
        isOpen = false;
    }

    @Override
    public byte[] read(int byteCount) {
        return incomingData.poll();
    }

    @Override
    public void write(byte[] data) {
        outgoingData.add(Arrays.copyOf(data, data.length));
    }

    // 测试辅助方法:模拟传感器发送数据到串口
    public void simulateSensorInput(byte[] data) {
        incomingData.add(data);
    }

    // 测试辅助方法:获取业务代码发送出去的数据,验证逻辑正确性
    public List<byte[]> getOutgoingData() {
        return outgoingData;
    }

    public boolean isOpen() {
        return isOpen;
    }
}

单元测试示例

在测试中把模拟适配器注入到你的业务逻辑类里,就能完全控制串口交互:

import org.junit.Test;
import static org.junit.Assert.*;

public class SensorCommunicatorTest {
    @Test
    public void testSensorDataReading() throws SerialPortException {
        // 初始化模拟串口
        MockSerialPortHandler mockPort = new MockSerialPortHandler();
        // 注入到你的业务类中
        SensorCommunicator communicator = new SensorCommunicator(mockPort);

        // 模拟传感器发送数据
        byte[] testData = new byte[]{0x01, 0x02, 0x03};
        mockPort.simulateSensorInput(testData);

        // 调用业务方法读取数据
        byte[] receivedData = communicator.readSensorData();

        // 验证读取结果正确
        assertArrayEquals(testData, receivedData);
        // 验证业务代码是否发送了预期指令(比如请求数据的指令)
        assertEquals(1, mockPort.getOutgoingData().size());
    }
}

方案二:用Mockito直接模拟JSSC的SerialPort类

如果不想写适配器,也可以用Mockito这类Mock框架直接模拟SerialPort类。不过要注意:jssc部分方法是final的,Mockito默认不支持模拟final方法,需要开启Mockito Inline支持(在依赖中添加mockito-inline包)。

示例代码:

import org.junit.Test;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

public class SensorCommunicatorMockitoTest {
    @Test
    public void testWithMockedSerialPort() throws SerialPortException {
        // 模拟SerialPort实例
        SerialPort mockSerialPort = mock(SerialPort.class);
        
        // 预设方法返回值:模拟打开串口成功
        when(mockSerialPort.openPort()).thenReturn(true);
        // 预设读取数据的返回值
        when(mockSerialPort.readBytes(3)).thenReturn(new byte[]{0x01, 0x02, 0x03});

        // 注入到业务类
        SensorCommunicator communicator = new SensorCommunicator(mockSerialPort);
        communicator.connect();
        byte[] data = communicator.readSensorData();

        // 验证结果
        assertArrayEquals(new byte[]{0x01, 0x02, 0x03}, data);
        // 验证openPort方法被调用过一次
        verify(mockSerialPort, times(1)).openPort();
    }
}

方案三:虚拟串口库(适合集成测试)

如果需要更接近真实环境的双向串口交互(比如模拟两个串口互相通信),可以用Java虚拟串口库,比如jSerialComm(它支持创建虚拟串口对)。不过这个方案更适合集成测试,单元测试用前面两种Mock方式更轻量高效。

示例代码(基于jSerialComm):

import com.fazecast.jSerialComm.SerialPort;

public class VirtualSerialTest {
    public static void main(String[] args) {
        // 创建一对虚拟串口,比如COM3和COM4
        SerialPort[] virtualPorts = SerialPort.createVirtualPortPair("COM3", "COM4");
        SerialPort sensorSidePort = virtualPorts[0];
        SerialPort testSidePort = virtualPorts[1];

        // 业务代码连接COM3,测试代码通过COM4发送/接收数据,模拟传感器行为
        sensorSidePort.openPort();
        testSidePort.openPort();

        // 测试代码发送模拟数据
        testSidePort.writeBytes(new byte[]{0x01, 0x02, 0x03}, 0, 3);
        // 业务代码读取数据...
    }
}

内容的提问来源于stack exchange,提问作者Gadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:02:16