消息队列系统测试报告

目录

一、项目背景

二、RabbitMQ介绍

1.什么是RabbitMQ?

2.RabbitMQ的工作流程是怎么样的?

3.项目设计

三、测试概述

[MQ 测试目标:](#MQ 测试目标:)

测试用例统计:

核心模块测试详情及代码示例:

[1. 数据库管理(DataBaseManagerTest)](#1. 数据库管理(DataBaseManagerTest))

[2. 消息文件管理(MessageFileManagerTest)](#2. 消息文件管理(MessageFileManagerTest))

[3. 内存数据中心(MemoryDataCenterTest)](#3. 内存数据中心(MemoryDataCenterTest))

[4. 虚拟主机(VirtualHostTest)](#4. 虚拟主机(VirtualHostTest))

[5. 客户端交互(MqClientTests)](#5. 客户端交互(MqClientTests))

结论


一、项目背景

基于RabbitMQ这一中间件的核心设计理念,使用SpringBoot简化开发,SQLite实现轻量级持久化数据,利用所学的生产者消费者模型为用户提供一个实现解耦合、削峰填谷功能的消息中间件,为用户提供九个相关的API,进行信道的连接、断开、交换机的创建和删除操作等;对交换机、队列、绑定和消息提供实现持久化需的功能,当程序重启, 保证上述内容不丢失,为用户提供数据安全性。客户端通过channel的创建,极大程度提高TCP连接的复用,达到长连接的效果。给客户端提供消息自动应答和手动应答的功能,极大提高消息传输的可靠性,满足对于数据可靠性要求⾼的场景。

二、RabbitMQ介绍

1.什么是RabbitMQ?

RabbitMQ:是一款基于 AMQP(Advanced Message Queuing Protocol)协议的开源消息队列中间件,由 Erlang 语言开发,以轻量级、高可靠性、灵活的路由机制和丰富的消息模式著称。它广泛应用于分布式系统中,实现服务间的异步通信、解耦和流量控制。
RabbitMQ介绍官⽹: RabbitMQ: One broker to queue them all | RabbitMQ

总结:RabbitMQ是一个消息中间件, 也是一个生产者消费者模型. 它最核心的功能就是负责接收, 存储并转发消息。

2.RabbitMQ的工作流程是怎么样的?

要了解RabbitMQ的工作流程那清楚记牢一张图就知道了!

3.项目设计

三、测试概述

MQ 测试目标:

测试类别 测试模块 测试目标
核心数据组件功能测试 MemoryDataCenter 1. 验证交换机、队列、绑定关系及消息的 CRUD 操作准确性(新增、查询、删除后的数据一致性) 2. 验证消息发送(sendMessage)、获取(pollMessage)及等待确认(addMessageWaitAck/removeMessageWaitAck)流程的完整性 3. 验证多线程并发操作下的数据安全性(无重复数据、数据丢失或错乱)
DataBaseManager 1. 验证数据库表初始化逻辑(默认交换机存在、队列和绑定表为空) 2. 验证交换机、队列、绑定关系的持久化存储与查询一致性(属性完整匹配,包括名称、类型、参数等) 3. 验证删除操作的有效性(删除后数据库中无残留数据)
MessageFileManager 1. 验证队列文件创建与销毁功能(文件及目录存在性校验) 2. 验证消息持久化(sendMessage)、读取(loadAllMessageFromQueue)及删除(deleteMessage)的准确性(消息内容、状态完整匹配) 3. 验证 GC 机制的有效性(清理无效消息后文件大小缩减,有效消息保留完整)
核心业务逻辑测试 VirtualHost 1. 验证交换机、队列的声明与删除功能(操作返回值正确性及数据状态一致性) 2. 验证队列与交换机的绑定(queueBind)和解绑(queueUnbind)逻辑(绑定关系正确建立与移除) 3. 验证消息发布(basicPublish)与消费(basicConsume)全流程(消息正确路由至目标队列,消费者能准确接收并处理) 4. 验证不同交换机类型(DIRECTFANOUTTOPIC)的路由规则有效性(消息按预期分发至匹配队列)
Router 1. 验证 TOPIC 类型交换机的路由键(RoutingKey)与绑定键(BindingKey)匹配规则(覆盖通配符 *# 的各种组合场景,确保匹配结果符合预期)
服务器与客户端交互测试 BrokerServer 1. 验证服务器对客户端连接的处理(TCP 连接建立、关闭及资源释放) 2. 验证服务器对不同类型请求的解析与响应(创建信道、声明交换机/队列、发布/消费消息等) 3. 验证服务器的并发处理能力(多客户端同时操作时的数据一致性)
客户端 Channel 1. 验证客户端与服务器的通信协议(请求格式、响应解析的正确性) 2. 验证消息发布(basicPublish)、订阅(basicConsume)、确认(Ack)等操作的完整性

测试用例统计:

测试类 测试用例数 测试结果
DataBaseManagerTest 7 全部通过
MessageFileManagerTest 6 全部通过
MemoryDataCenterTest 6 全部通过
RouterTest 16 全部通过
VirtualHostTest 11 全部通过
MqClientTests 6 全部通过
总计 52 全部通过

核心模块测试详情及代码示例:

1. 数据库管理(DataBaseManagerTest)

该测试类主要验证数据库层面对交换机、队列和绑定关系的 CRUD 操作。

关键测试代码:

java 复制代码
/**
 * DataBaseManager测试类,用于验证数据库管理器对交换机、队列、绑定关系的CRUD操作
 * 测试基于内存数据库(如H2),通过MyBatis映射文件与数据库交互
 */
@SpringBootTest
public class DataBaseManagerTest {
    // 数据库管理器实例,作为测试核心操作对象(静态实例确保全局唯一)
    private static DataBaseManager dataBaseManager = new DataBaseManager();

    /**
     * 测试前置方法,在每个测试用例执行前初始化Spring上下文并初始化数据库
     * 1. 启动Spring应用上下文,用于获取MyBatis的Mapper实例
     * 2. 调用DataBaseManager的init()方法,创建数据表(若不存在)并插入默认数据
     */
    @BeforeEach
    public void setUp() {
        // 初始化Spring上下文,为DataBaseManager提供Mapper实例
        MqApplication.context = SpringApplication.run(MqApplication.class);
        // 初始化数据库(创建表结构和默认数据)
        dataBaseManager.init();
    }

    /**
     * 测试后置方法,在每个测试用例执行后清理资源
     * 1. 关闭Spring上下文,释放数据库连接和端口资源
     * 2. 删除数据库文件及目录,避免测试数据残留影响后续测试
     */
    @AfterEach
    public void tearDown() {
        // 关闭Spring上下文,释放资源
        MqApplication.context.close();
        // 删除数据库文件(meta.db)及数据目录
        dataBaseManager.deleteDd();
    }

    /**
     * 测试数据库表初始化功能
     * 验证初始化后的数据表状态:
     * - 交换机表包含1条默认交换机(名称为空字符串)
     * - 队列表和绑定表初始状态为空
     */
    @Test
    public void testInitTable() {
        // 查询所有交换机、队列和绑定关系
        List<Exchange> exchanges = dataBaseManager.selectAllExchanges();
        List<MSGQueue> msgQueues = dataBaseManager.selectAllQueues();
        List<Binding> bindings = dataBaseManager.selectAllBindings();

        // 验证默认交换机存在(初始化时插入的空名称交换机)
        Assertions.assertEquals(1, exchanges.size());
        Assertions.assertEquals("", exchanges.get(0).getName());
        // 验证队列表初始为空
        Assertions.assertEquals(0, msgQueues.size());
        // 验证绑定表初始为空
        Assertions.assertEquals(0, bindings.size());
    }

    /**
     * 测试交换机插入功能
     * 验证流程:
     * 1. 创建测试交换机并插入数据库
     * 2. 查询所有交换机,验证总数为2(默认交换机+新插入的交换机)
     * 3. 验证新插入交换机的名称和类型与预期一致
     */
    @Test
    public void testInsertExchange() {
        // 创建测试交换机(名称为"testExchange",类型为FANOUT)
        Exchange exchange = createTestExchange("testExchange");
        // 插入交换机到数据库
        dataBaseManager.insertExchange(exchange);
        // 查询所有交换机
        List<Exchange> exchangeList = dataBaseManager.selectAllExchanges();
        
        // 验证交换机总数(默认1条+新增1条)
        Assertions.assertEquals(2, exchangeList.size());
        // 获取新增的交换机并验证属性
        Exchange newExchange = exchangeList.get(1);
        Assertions.assertEquals("testExchange", newExchange.getName());
        Assertions.assertEquals(ExchangeType.FANOUT, newExchange.getType());
    }

    /**
     * 测试队列删除功能
     * 验证流程:
     * 1. 创建测试队列并插入数据库
     * 2. 验证队列插入成功(查询结果数量为1)
     * 3. 执行删除操作后,验证队列已从数据库中移除(查询结果数量为0)
     */
    @Test
    public void testDeleteQueue() {
        // 创建测试队列(名称为"testQueue")
        MSGQueue queue = createTestQueue("testQueue");
        // 插入队列到数据库
        dataBaseManager.insertQueue(queue);
        // 验证队列插入成功
        List<MSGQueue> queueList = dataBaseManager.selectAllQueues();
        Assertions.assertEquals(1, queueList.size());
        
        // 执行删除队列操作
        dataBaseManager.deleteQueue("testQueue");
        // 验证队列已被删除
        queueList = dataBaseManager.selectAllQueues();
        Assertions.assertEquals(0, queueList.size());
    }

    /**
     * 创建测试交换机
     * @param exchangeName 交换机名称
     * @return 构造好的交换机实例(FANOUT类型,持久化,非自动删除)
     */
    private Exchange createTestExchange(String exchangeName) {
        Exchange exchange = new Exchange();
        exchange.setName(exchangeName);
        exchange.setType(ExchangeType.FANOUT);
        exchange.setAutoDelete(false);
        exchange.setDurable(true);
        exchange.setArguments("aaa", 1);
        exchange.setArguments("bbb", 2);
        return exchange;
    }

    /**
     * 创建测试队列
     * @param queueName 队列名称
     * @return 构造好的队列实例(持久化,非排他,非自动删除)
     */
    private MSGQueue createTestQueue(String queueName) {
        MSGQueue queue = new MSGQueue();
        queue.setName(queueName);
        queue.setDurable(true);
        queue.setAutoDelete(false);
        queue.setExclusive(false);
        queue.setArguments("aaa", 1);
        queue.setArguments("bbb", 2);
        return queue;
    }
}

2. 消息文件管理(MessageFileManagerTest)

验证消息在文件系统中的持久化存储、读取、删除及 GC 压缩功能。

关键测试代码:

java 复制代码
/**
 * MessageFileManager测试类,用于验证消息文件管理器的核心功能
 * 包括消息的持久化存储、读取、删除及垃圾回收(GC)等文件操作
 */
@Slf4j
@SpringBootTest
class MessageFileManagerTest {
    // 消息文件管理器实例,测试操作的核心对象
    private MessageFileManager messageFileManager = new MessageFileManager();
    // 测试用队列名称常量,避免硬编码
    private static final String queueName1 = "testQueue1";

    /**
     * 测试前置方法,在每个测试用例执行前初始化队列文件
     * 创建测试队列对应的目录和数据文件,为后续测试提供基础环境
     * @throws IOException 当创建队列文件失败时抛出
     */
    @BeforeEach
    public void setUp() throws IOException {
        messageFileManager.createQueueFile(queueName1);
    }

    /**
     * 测试后置方法,在每个测试用例执行后清理队列文件
     * 删除测试过程中生成的队列目录及数据文件,避免影响其他测试
     * @throws IOException 当删除队列文件失败时抛出
     */
    @AfterEach
    public void tearDown() throws IOException {
        messageFileManager.destroyQueueFiles(queueName1);
    }

    /**
     * 测试消息发送与持久化功能
     * 验证流程:
     * 1. 创建测试消息和队列
     * 2. 发送消息到队列并持久化到文件
     * 3. 验证统计信息中消息总数是否正确
     * 4. 验证从文件加载的消息与发送的消息内容一致
     * @throws IOException 当文件操作失败时抛出
     * @throws MqException 当MQ操作异常时抛出
     * @throws ClassNotFoundException 当消息反序列化失败时抛出
     */
    @Test
    public void testSendMessage() throws IOException, MqException, ClassNotFoundException {
        // 创建测试消息(内容为"testMessage")
        Message message = createTestMessage("testMessage");
        // 创建测试队列(使用预定义的队列名)
        MSGQueue queue = createTestQueue(queueName1);

        // 发送消息到队列,触发持久化操作
        messageFileManager.sendMessage(queue, message);

        // 反射调用私有方法读取统计信息(验证消息总数)
        MessageFileManager.Stat stat = ReflectionTestUtils.invokeMethod(messageFileManager, "readStat", queueName1);
        Assertions.assertEquals(1, stat.totalCount);

        // 从文件加载所有消息并验证
        LinkedList<Message> messages = messageFileManager.loadAllMessageFromQueue(queueName1);
        Assertions.assertEquals(1, messages.size()); // 验证消息数量
        Assertions.assertArrayEquals(message.getBody(), messages.get(0).getBody()); // 验证消息内容
    }

    /**
     * 测试消息文件的垃圾回收(GC)功能
     * 验证流程:
     * 1. 向队列发送100条测试消息
     * 2. 记录GC前的文件大小
     * 3. 删除偶数下标的消息(逻辑删除)
     * 4. 执行GC操作(清理无效消息,压缩文件)
     * 5. 验证GC后的文件大小小于GC前(确认压缩效果)
     * @throws IOException 当文件操作失败时抛出
     * @throws MqException 当MQ操作异常时抛出
     * @throws ClassNotFoundException 当消息反序列化失败时抛出
     */
    @Test
    public void testGC() throws IOException, MqException, ClassNotFoundException {
        // 创建测试队列
        MSGQueue queue = createTestQueue(queueName1);
        // 存储所有发送的消息,用于后续删除操作
        List<Message> messages = new LinkedList<>();
        // 发送100条测试消息
        for (int i = 0; i < 100; i++) {
            Message message = createTestMessage("testMessage" + i);
            messageFileManager.sendMessage(queue, message);
            messages.add(message);
        }

        // 记录GC前的数据文件大小
        File beforeGCFile = new File("./data/" + queueName1 + "/queue_data.txt");
        long beforeGCLength = beforeGCFile.length();

        // 删除偶数下标的消息(逻辑删除,标记为无效)
        for (int i = 0; i < 100; i += 2) {
            messageFileManager.deleteMessage(queue, messages.get(i));
        }

        // 执行GC操作,清理无效消息并压缩文件
        messageFileManager.gc(queue);
        // 验证GC后的文件大小小于GC前
        File afterGCFile = new File("./data/" + queueName1 + "/queue_data.txt");
        Assertions.assertTrue(afterGCFile.length() < beforeGCLength);
    }

    /**
     * 创建测试队列
     * @param queueName 队列名称
     * @return 构造好的测试队列实例(持久化、非自动删除)
     */
    private MSGQueue createTestQueue(String queueName) {
        MSGQueue queue = new MSGQueue();
        queue.setName(queueName);
        queue.setDurable(true);
        queue.setAutoDelete(false);
        queue.setExclusive(false);
        return queue;
    }

    /**
     * 创建测试消息
     * @param content 消息内容
     * @return 构造好的测试消息实例(包含指定内容的字节数组)
     */
    private Message createTestMessage(String content) {
        return Message.createMessageWithId("testRoutingKey", null, content.getBytes());
    }
}

3. 内存数据中心(MemoryDataCenterTest)

测试内存中交换机、队列、消息的管理及消息收发机制。

关键测试代码:

java 复制代码
/**
 * MemoryDataCenter测试类,用于验证内存数据中心的核心功能
 * 包括交换机管理、消息发送与接收等内存数据操作
 */
@SpringBootTest
class MemoryDataCenterTest {
    // 内存数据中心实例,测试操作的核心对象
    private MemoryDataCenter memoryDataCenter = null;

    /**
     * 测试前置方法,在每个测试用例执行前初始化内存数据中心
     * 确保每个测试用例都使用全新的内存数据中心实例,避免测试间相互干扰
     */
    @BeforeEach
    public void setUp() {
        memoryDataCenter = new MemoryDataCenter();
    }

    /**
     * 测试交换机的增删查功能
     * 验证流程:
     * 1. 创建测试交换机并插入内存数据中心
     * 2. 查询交换机并验证其与插入的交换机一致
     * 3. 删除交换机后验证查询结果为null
     */
    @Test
    public void testExchange() {
        // 创建测试交换机(实际代码中应有createTestExchange方法,此处假设已实现)
        Exchange expectedExchange = createTestExchange("testExchange");
        // 插入交换机到内存数据中心
        memoryDataCenter.insertExchange(expectedExchange);
        
        // 查询交换机并验证是否与插入的一致
        Exchange actualExchange = memoryDataCenter.getExchange("testExchange");
        Assertions.assertEquals(expectedExchange, actualExchange);
        
        // 删除交换机
        memoryDataCenter.deleteExchange("testExchange");
        // 验证交换机已被删除
        actualExchange = memoryDataCenter.getExchange("testExchange");
        Assertions.assertNull(actualExchange);
    }

    /**
     * 测试消息发送到队列及从队列接收消息的功能
     * 验证流程:
     * 1. 创建测试队列和10条测试消息
     * 2. 将消息发送到队列并记录预期消息列表
     * 3. 从队列中取出所有消息并记录实际消息列表
     * 4. 验证实际消息与预期消息的数量和内容完全一致
     */
    @Test
    public void testSendMessage() {
        // 创建测试队列(实际代码中应有createTestQueue方法,此处假设已实现)
        MSGQueue queue = createTestQueue("testQueue");
        // 存储预期发送的消息列表
        List<Message> expectedMessages = new ArrayList<>();
        // 创建10条测试消息并发送到队列
        for (int i = 0; i < 10; i++) {
            Message message = createTestMessage("testMessage" + i); // 假设存在创建测试消息的方法
            memoryDataCenter.sendMessage(queue, message);
            expectedMessages.add(message);
        }

        // 存储从队列中实际取出的消息列表
        List<Message> actualMessages = new ArrayList<>();
        // 循环从队列中获取消息直到队列为空
        while (true) {
            Message message = memoryDataCenter.pollMessage("testQueue");
            if (message == null) break;
            actualMessages.add(message);
        }

        // 验证消息数量一致
        Assertions.assertEquals(expectedMessages.size(), actualMessages.size());
        // 验证每条消息内容一致
        for (int i = 0; i < expectedMessages.size(); i++) {
            Assertions.assertEquals(expectedMessages.get(i), actualMessages.get(i));
        }
    }

    /**
     * 创建测试交换机(实际实现方法,用于测试)
     * @param exchangeName 交换机名称
     * @return 构造好的测试交换机实例
     */
    private Exchange createTestExchange(String exchangeName) {
        Exchange exchange = new Exchange();
        exchange.setName(exchangeName);
        exchange.setType(ExchangeType.DIRECT); // 示例使用DIRECT类型
        exchange.setDurable(true);
        exchange.setAutoDelete(false);
        return exchange;
    }

    /**
     * 创建测试队列(实际实现方法,用于测试)
     * @param queueName 队列名称
     * @return 构造好的测试队列实例
     */
    private MSGQueue createTestQueue(String queueName) {
        MSGQueue queue = new MSGQueue();
        queue.setName(queueName);
        queue.setDurable(true);
        queue.setAutoDelete(false);
        return queue;
    }

    /**
     * 创建测试消息(实际实现方法,用于测试)
     * @param content 消息内容
     * @return 构造好的测试消息实例
     */
    private Message createTestMessage(String content) {
        return Message.createMessageWithId("testRoutingKey", null, content.getBytes());
    }
}

4. 虚拟主机(VirtualHostTest)

验证虚拟主机中交换机、队列的声明 / 删除、绑定 / 解绑及消息发布消费流程。

关键测试代码:

java 复制代码
/**
 * VirtualHost测试类,用于验证虚拟主机(VirtualHost)的核心功能
 * 包括交换机声明、队列操作、消息发布与消费等MQ核心流程
 */
class VirtualHostTest {
    // 虚拟主机实例,测试用例的核心操作对象
    private VirtualHost virtualHost = null;

    /**
     * 测试前置方法,在每个测试用例执行前初始化环境
     * 1. 启动Spring应用上下文,加载必要的Bean组件
     * 2. 创建名为"test"的虚拟主机实例
     */
    @BeforeEach
    public void setUp() {
        // 初始化Spring上下文,为依赖注入提供支持
        MqApplication.context = SpringApplication.run(MqApplication.class);
        // 创建测试用虚拟主机
        virtualHost = new VirtualHost("test");
    }

    /**
     * 测试后置方法,在每个测试用例执行后清理环境
     * 1. 关闭Spring应用上下文释放资源
     * 2. 销毁虚拟主机实例
     * 3. 删除测试过程中生成的data目录(消息持久化数据)
     * @throws IOException 当删除目录时发生I/O错误时抛出
     */
    @AfterEach
    public void tearDown() throws IOException {
        // 关闭Spring上下文
        MqApplication.context.close();
        // 置空虚拟主机引用,帮助GC回收
        virtualHost = null;
        // 清理测试产生的持久化文件
        File file = new File("./data");
        FileUtils.deleteDirectory(file);
    }

    /**
     * 测试先订阅队列后发送消息的场景
     * 验证流程:
     * 1. 声明DIRECT类型交换机和队列
     * 2. 先注册消费者订阅队列
     * 3. 发送消息到交换机
     * 4. 验证消费者能否正确接收并处理消息
     * @throws InterruptedException 当线程休眠被中断时抛出
     */
    @Test
    public void testBasicConsume1() throws InterruptedException {
        // 声明DIRECT类型持久化交换机
        virtualHost.exchangeDeclare("testExchange", ExchangeType.DIRECT, true, false, null);
        // 声明持久化队列
        virtualHost.queueDeclare("testQueue", true, false, false, null);

        // 先订阅队列,设置自动确认模式,定义消息处理回调
        boolean ok = virtualHost.basicConsume("testConsumerTag", "testQueue", true, new Consumer() {
            /**
             * 消息处理回调方法,验证消息属性和内容
             * @param consumerTag 消费者标签,标识当前消费者
             * @param props 消息属性,包含路由键等元数据
             * @param body 消息体内容
             */
            @Override
            public void handleDelivery(String consumerTag, BasicProperties props, byte[] body) {
                // 验证消息路由键是否正确
                Assertions.assertEquals("testQueue", props.getRoutingKey());
                // 验证消息内容是否与发送内容一致
                Assertions.assertArrayEquals("hello".getBytes(), body);
            }
        });
        // 验证订阅操作是否成功
        Assertions.assertTrue(ok);

        // 休眠500ms等待订阅生效
        Thread.sleep(500);
        // 发送消息到交换机,路由键为队列名
        boolean publishOk = virtualHost.basicPublish("testExchange", "testQueue", null, "hello".getBytes());
        // 验证消息发送是否成功
        Assertions.assertTrue(publishOk);
    }

    /**
     * 测试FANOUT类型交换机的消息广播功能
     * 验证流程:
     * 1. 声明FANOUT类型交换机
     * 2. 创建两个队列并绑定到交换机(FANOUT交换机忽略绑定键)
     * 3. 发送消息到交换机
     * 4. 验证两个队列的消费者都能收到消息
     * @throws InterruptedException 当线程操作被中断时抛出
     */
    @Test
    public void testBasicConsumeFanout() throws InterruptedException {
        // 声明FANOUT类型持久化交换机(广播模式)
        virtualHost.exchangeDeclare("testExchange", ExchangeType.FANOUT, true, false, null);
        // 声明第一个队列并绑定到交换机
        virtualHost.queueDeclare("testQueue1", true, false, false, null);
        virtualHost.queueBind("testQueue1", "testExchange", "");
        // 声明第二个队列并绑定到交换机
        virtualHost.queueDeclare("testQueue2", true, false, false, null);
        virtualHost.queueBind("testQueue2", "testExchange", "");

        // 向FANOUT交换机发送消息(路由键无效,可留空)
        virtualHost.basicPublish("testExchange", "", null, "hello".getBytes());

        // 第一个队列的消费者验证消息
        virtualHost.basicConsume("tag1", "testQueue1", true, (tag, props, body) -> 
            Assertions.assertArrayEquals("hello".getBytes(), body));
        // 第二个队列的消费者验证消息(FANOUT交换机应广播给所有绑定队列)
        virtualHost.basicConsume("tag2", "testQueue2", true, (tag, props, body) -> 
            Assertions.assertArrayEquals("hello".getBytes(), body));
    }
}

5. 客户端交互(MqClientTests)

测试客户端与服务器的完整交互流程,包括连接建立、通道创建、消息生产和消费。

关键测试代码:

java 复制代码
/**
 * MQ客户端测试类,用于验证客户端与MQ服务器之间的交互功能
 * 包括连接建立、通道创建、交换机/队列操作及消息收发等核心流程
 */
public class MqClientTests {
    // Broker服务器实例,用于在测试中启动和停止MQ服务
    private BrokerServer brokerServer = null;
    // 连接工厂,用于创建客户端与服务器的连接
    private ConnectionFactory factory = null;
    // 用于运行BrokerServer的线程,避免服务器启动阻塞测试流程
    private Thread t = null;

    /**
     * 测试前置方法,在每个测试用例执行前运行
     * 1. 启动Spring应用上下文
     * 2. 初始化并启动Broker服务器(在独立线程中运行)
     * 3. 配置连接工厂,设置服务器地址和端口
     * @throws IOException 当I/O操作失败时抛出
     */
    @BeforeEach
    public void setUp() throws IOException {
        // 启动Spring应用上下文,加载必要的Bean
        MqApplication.context = SpringApplication.run(MqApplication.class);
        // 创建Broker服务器实例,监听9090端口
        brokerServer = new BrokerServer(9090);
        // 创建线程运行Broker服务器(start()方法会进入循环,需独立线程)
        t = new Thread(() -> {
            try {
                brokerServer.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        t.start();

        // 初始化连接工厂,配置服务器地址和端口
        factory = new ConnectionFactory();
        factory.setHost("127.0.0.1");
        factory.setPort(9090);
    }

    /**
     * 测试后置方法,在每个测试用例执行后运行
     * 1. 停止Broker服务器
     * 2. 关闭Spring应用上下文
     * 3. 清理测试生成的data目录(消息持久化数据)
     * @throws IOException 当I/O操作失败时抛出
     */
    @AfterEach
    public void tearDown() throws IOException {
        // 停止Broker服务器
        brokerServer.stop();
        // 关闭Spring应用上下文
        MqApplication.context.close();
        // 删除测试过程中生成的data目录及文件
        File file = new File("./data");
        FileUtils.deleteDirectory(file);
    }

    /**
     * 测试消息发送与消费的完整流程
     * 1. 建立连接和通道
     * 2. 声明交换机和队列
     * 3. 发送测试消息到交换机
     * 4. 订阅队列并验证消息内容
     * @throws IOException 当I/O操作失败时抛出
     * @throws MqException 当MQ操作失败时抛出
     * @throws InterruptedException 当线程休眠被中断时抛出
     */
    @Test
    public void testMessage() throws IOException, MqException, InterruptedException {
        // 创建与服务器的连接
        Connection connection = factory.newConnection();
        // 创建通道(基于连接的通信通道)
        Channel channel = connection.createChannel();

        // 声明一个DIRECT类型的持久化交换机
        channel.exchangeDeclare("testExchange", ExchangeType.DIRECT, true, false, null);
        // 声明一个持久化队列
        channel.queueDeclare("testQueue", true, false, false, null);

        // 准备测试消息内容
        byte[] requestBody = "hello".getBytes();
        // 发送消息到指定交换机,指定路由键为队列名(DIRECT交换机根据路由键匹配队列)
        channel.basicPublish("testExchange", "testQueue", null, requestBody);

        // 订阅队列,设置自动确认,并定义消息处理回调
        channel.basicConsume("testQueue", true, new Consumer() {
            /**
             * 消息处理回调方法,验证消费的消息内容与发送的一致
             * @param consumerTag 消费者标签,用于标识消费者
             * @param basicProperties 消息属性
             * @param body 消息体内容
             */
            @Override
            public void handleDelivery(String consumerTag, BasicProperties basicProperties, byte[] body) {
                // 断言消费的消息体与发送的一致
                Assertions.assertArrayEquals(requestBody, body);
            }
        });

        // 休眠500ms,等待消息处理完成
        Thread.sleep(500);
        // 关闭通道和连接
        channel.close();
        connection.close();
    }
}

结论

所有 52 个测试用例均通过验证,覆盖了 MQ 系统的核心功能点,包括交换机 / 队列管理、消息存储与传输、路由规则、客户端交互等。测试结果表明,系统各模块功能正常,模块间交互符合预期,整体表现稳定可靠,能够满足消息队列的基本功能需求。