知識庫 / Spring RSS 訂閱

測試 Spring JMS

Spring,Testing
HongKong
6
11:58 AM · Dec 06 ,2025

1. 概述

在本教程中,我們將創建一個簡單的 Spring 應用,該應用連接到 ActiveMQ 以發送和接收消息。 我們將重點關注測試此應用以及對 Spring JMS 整體測試的不同方法。

2. 應用設置

首先,讓我們創建一個基本的應用程序,用於測試。我們需要添加必要的依賴項並實現消息處理。

2.1. 依賴項

讓我們為項目的 <em >pom.xml</em> 添加所需的依賴項。 我們需要使用 Spring JMS 才能監聽 JMS 消息。 我們將使用 ActiveMQ-Junit 啓動嵌入式 ActiveMQ 實例,用於部分測試,並使用 TestContainers 在其他測試中運行 ActiveMQ Docker 容器:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jms</artifactId>
    <version>4.3.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.apache.activemq.tooling</groupId>
    <artifactId>activemq-junit</artifactId>
    <version>5.16.5</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.17.3</version>
    <scope>test</scope>
</dependency>

2.2. 應用代碼

現在,讓我們創建一個可以監聽消息的 Spring 應用:

@ComponentScan
public class JmsApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(JmsApplication.class);
    }
}

我們需要創建一個配置類,並使用 @EnableJms 註解啓用 JMS,同時配置 ConnectionFactory 以連接到我們的 ActiveMQ 實例。

@Configuration
@EnableJms
public class JmsConfig {

    @Bean
    public JmsListenerContainerFactory<?> jmsListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        return factory;
    }

    @Bean
    public ConnectionFactory connectionFactory() {
        return new ActiveMQConnectionFactory("tcp://localhost:61616");
    }

    @Bean
    public JmsTemplate jmsTemplate() {
        return new JmsTemplate(connectionFactory());
    }
}
<p>在完成以上步驟後,我們接下來創建能夠接收和處理消息的監聽器。</p>
@Component
public class MessageListener {

    private static final Logger logger = LoggerFactory.getLogger(MessageListener.class);

    @JmsListener(destination = "queue-1")
    public void sampleJmsListenerMethod(TextMessage message) throws JMSException {
        logger.info("JMS listener received text message: {}", message.getText());
    }
}

我們還需要一個可以發送消息的類:

@Component
public class MessageSender {

    @Autowired
    private JmsTemplate jmsTemplate;

    private static final Logger logger = LoggerFactory.getLogger(MessageSender.class);

    public void sendTextMessage(String destination, String message) {
        logger.info("Sending message to {} destination with text {}", destination, message);
        jmsTemplate.send(destination, s -> s.createTextMessage(message));
    }
}

3. 使用嵌入式 ActiveMQ 進行測試

讓我們測試我們的應用程序。我們首先將使用嵌入式 ActiveMQ 實例。 讓我們創建一個測試類並添加一個 JUnit Rule,該 Rule 管理我們的 ActiveMQ 實例:

@RunWith(SpringRunner.class)
public class EmbeddedActiveMqTests4 {

    @ClassRule
    public static EmbeddedActiveMQBroker embeddedBroker = new EmbeddedActiveMQBroker();

    @Test
    public void test() {
    }

    // ...
}

讓我們運行此空測試並檢查日誌。 我們可以看到一個嵌入式代理已啓動,用於我們的測試

INFO | Starting embedded ActiveMQ broker: embedded-broker
INFO | Using Persistence Adapter: MemoryPersistenceAdapter
INFO | Apache ActiveMQ 5.14.1 (embedded-broker, ID:DESKTOP-52539-254421135-0:1) is starting
INFO | Apache ActiveMQ 5.14.1 (embedded-broker, ID:DESKTOP-52539-254421135-0:1) started
INFO | For help or more information please see: http://activemq.apache.org
INFO | Connector vm://embedded-broker started
INFO | Successfully connected to vm://embedded-broker?create=false

測試類執行完畢後,中介已被停止。

我們需要配置應用程序,使其能夠連接到該 ActiveMQ 實例,以便正確測試我們的 MessageListener 類和 MessageSender 類:

@Configuration
@EnableJms
static class TestConfiguration {
    @Bean
    public JmsListenerContainerFactory<?> jmsListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory());
        return factory;
    }

    @Bean
    public ConnectionFactory connectionFactory() {
        return new ActiveMQConnectionFactory(embeddedBroker.getVmURL());
    }

    @Bean
    public JmsTemplate jmsTemplate() {
        return new JmsTemplate(connectionFactory());
    }
}

這個類使用一個特殊的 ConnectionFactory,它從我們的嵌入式代理獲取 URL。現在,我們需要通過在包含我們測試的類上添加 @ContextConfiguration 註解來使用這個配置:

@ContextConfiguration(classes = { TestConfiguration.class, MessageSender.class }) public class EmbeddedActiveMqTests {

3.1. 發送消息

讓我們編寫第一個測試並檢查 MessageSender 類功能的有效性。首先,我們需要通過將該類實例注入為字段來獲取一個引用:

@Autowired
private MessageSender messageSender;

讓我們向 ActiveMQ 發送一條簡單的消息,並添加斷言來檢查其功能:

@Test
public void whenSendingMessage_thenCorrectQueueAndMessageText() throws JMSException {
    String queueName = "queue-2";
    String messageText = "Test message";

    messageSender.sendTextMessage(queueName, messageText);

    assertEquals(1, embeddedBroker.getMessageCount(queueName));
    TextMessage sentMessage = embeddedBroker.peekTextMessage(queueName);
    assertEquals(messageText, sentMessage.getText());
}

現在我們確定我們的 MessageSender 正確工作,因為隊列中包含一個確切的條目,該條目在發送消息後包含正確的文本。

3.2. 接收消息

讓我們檢查一下我們的監聽器類。 我們先創建一個新的測試方法,並使用嵌入式代理髮送一條消息。 我們的監聽器設置為將“queue-1”用作目標,因此我們需要在這裏確保使用相同的名稱。

讓我們使用 Mockito 來檢查監聽器的行為。 我們將使用 @SpyBean 註解來獲取 MessageListener 的實例:

@SpyBean
private MessageListener messageListener;

然後,我們將檢查方法是否被調用,並使用 ArgumentCaptor 捕獲接收到的方法參數。

@Test
public void whenListening_thenReceivingCorrectMessage() throws JMSException {
    String queueName = "queue-1";
    String messageText = "Test message";

    assertEquals(0, embeddedBroker.getDestination(queueName).getDestinationStatistics().getDispatched().getCount());
    assertEquals(0, embeddedBroker.getDestination(queueName).getDestinationStatistics().getMessages().getCount());

    embeddedBroker.pushMessage(queueName, messageText);

    ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(TextMessage.class);
    Mockito.verify(messageListener, Mockito.timeout(100))
        .sampleJmsListenerMethod(messageCaptor.capture());

    TextMessage receivedMessage = messageCaptor.getValue();
    assertEquals(messageText, receivedMessage.getText());

    assertEquals(1, embeddedBroker.getDestination(queueName).getDestinationStatistics().getDispatched().getCount());
    assertEquals(0, embeddedBroker.getDestination(queueName).getDestinationStatistics().getMessages().getCount());
}

我們現在可以運行測試,並且它們全部通過。

4. 使用 TestContainers 進行測試

讓我們看看另一種在 Spring 應用程序中測試 JMS 的方法。 我們可以使用 TestContainers 運行一個 ActiveMQ Docker 容器,並在我們的測試中連接到它。

讓我們創建一個新的測試類,並將 Docker 容器作為 JUnit 規則包含其中:

@RunWith(SpringRunner.class)
public class TestContainersActiveMqTests {

    @ClassRule
    public static GenericContainer<?> activeMqContainer 
      = new GenericContainer<>(DockerImageName.parse("rmohr/activemq:5.14.3")).withExposedPorts(61616);

    @Test
    public void test() throws JMSException {
    }
}

讓我們運行此測試並檢查日誌。 我們可以看到與 TestContainers 相關的某些信息,因為它是拉取指定的 Docker 鏡像,並且啓動容器:

INFO | Creating container for image: rmohr/activemq:5.14.3
INFO | Container rmohr/activemq:5.14.3 is starting: e9b0ddcd45c54fc9994aff99d734d84b5fae14b55fdc70887c4a2c2309b229a7
INFO | Container rmohr/activemq:5.14.3 started in PT2.635S

讓我們創建一個類似於我們使用 ActiveMQ 時創建的配置類。唯一的區別在於 ConnectionFactory 的配置。

@Bean
public ConnectionFactory connectionFactory() {
    String brokerUrlFormat = "tcp://%s:%d";
    String brokerUrl = String.format(brokerUrlFormat, activeMqContainer.getHost(), activeMqContainer.getFirstMappedPort());
    return new ActiveMQConnectionFactory(brokerUrl);
}

4.1. 發送消息

讓我們測試我們的 MessageSender 類,看看它是否能與這個 Docker 容器一起使用。 這一次,我們不能使用 EmbeddedBroker 上的方法,但 Spring 的 JmsTemplate 也很容易使用:

@Autowired
private MessageSender messageSender;

@Autowired
private JmsTemplate jmsTemplate;

@Test
public void whenSendingMessage_thenCorrectQueueAndMessageText() throws JMSException {
    String queueName = "queue-2";
    String messageText = "Test message";

    messageSender.sendTextMessage(queueName, messageText);

    Message sentMessage = jmsTemplate.receive(queueName);
    Assertions.assertThat(sentMessage).isInstanceOf(TextMessage.class);

    assertEquals(messageText, ((TextMessage) sentMessage).getText());
}

我們可以使用 JmsTemplate 讀取隊列中的內容並檢查我們的類是否已正確發送消息。

4.2. 接收消息

測試我們的監聽器類與監聽器本身並沒有本質區別。 使用 JmsTemplate 發送消息並驗證監聽器是否接收到了正確的文本:

@SpyBean
private MessageListener messageListener;

@Test
public void whenListening_thenReceivingCorrectMessage() throws JMSException {
    String queueName = "queue-1";
    String messageText = "Test message";

    jmsTemplate.send(queueName, s -> s.createTextMessage(messageText));

    ArgumentCaptor<TextMessage> messageCaptor = ArgumentCaptor.forClass(TextMessage.class);

    Mockito.verify(messageListener, Mockito.timeout(100)).sampleJmsListenerMethod(messageCaptor.capture());

    TextMessage receivedMessage = messageCaptor.getValue();
    assertEquals(messageText, receivedMessage.getText());
}

5. 結論

在本文中,我們創建了一個基本的應用程序,該應用程序可以使用 Spring JMS 發送和接收消息。 此外,我們還討論了兩種測試該應用程序的方法。

首先,我們使用了嵌入式 ActiveMQ 實例,它還提供了與消息代理進行交互的一些便捷方法。 其次,我們使用 TestContainers 測試我們的代碼,並使用一個 Docker 容器模擬了更真實的場景。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.