測試 Spring JMS
一、概述
在本教程中,我們將創建一個連接到 ActiveMQ 以發送和接收消息的簡單 Spring 應用程序。我們將專注於測試這個應用程序以及整體測試 Spring JMS 的不同方法。
2. 應用程序設置
首先,讓我們創建一個可用於測試的基本應用程序。我們需要添加必要的依賴項並實現消息處理。
2.1。依賴項
讓我們將所需的依賴項添加到項目的pom.xml
中。我們需要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());
}
}
在此之後,讓我們創建可以接收和處理消息的偵聽器:
@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 來管理我們的 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";
embeddedBroker.pushMessage(queueName, messageText);
assertEquals(1, embeddedBroker.getMessageCount(queueName));
ArgumentCaptor<TextMessage> messageCaptor = ArgumentCaptor.forClass(TextMessage.class);
Mockito.verify(messageListener, Mockito.timeout(100)).sampleJmsListenerMethod(messageCaptor.capture());
TextMessage receivedMessage = messageCaptor.getValue();
assertEquals(messageText, receivedMessage.getText());
}
我們現在可以運行測試,它們都通過了。
4. 使用 TestContainer 進行測試
讓我們看看另一種在 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 容器更好地模擬真實場景。
與往常一樣,這些示例的源代碼可在 GitHub 上獲得。