spring-boot - Apache Camel SNMP 路由的简单单元测试
问题描述
我在编写一个有效的 Camel Spring-Boot 单元测试时遇到了一些麻烦,它测试了一个简单的 SNMP 路由。这是我到目前为止所拥有的:
SnmpRoute.kt
open class SnmpRoute(private val snmpProperties: SnmpProperties, private val repository: IPduEventRepository) : RouteBuilder() {
@Throws(Exception::class)
override fun configure() {
logger.debug("Initialising with properties [{}]", snmpProperties)
from("snmp:0.0.0.0:1161?protocol=udp&type=TRAP")
.process { exchange ->
// do stuff
}
.bean(repository, "save")
}
}
SnmpRouteTest.kt
@CamelSpringBootTest
@SpringBootApplication
@EnableAutoConfiguration
open class SnmpRouteTest : CamelTestSupport() {
object SnmpConstants {
const val SNMP_TRAP = "<snmp><entry><oid>...datadatadata...</oid><value>123456</value></entry></snmp>"
const val MOCK_SNMP_ENDPOINT = "mock:snmp"
}
@Mock
lateinit var snmpProperties: SnmpProperties
@Mock
lateinit var repository: IPduEventRepository
@InjectMocks
lateinit var snmpRoute: SnmpRoute
@EndpointInject(SnmpConstants.MOCK_SNMP_ENDPOINT)
lateinit var mock: MockEndpoint
@Before
fun setup() {
initMocks(this)
}
@Throws(Exception::class)
override fun createRouteBuilder(): RouteBuilder {
return snmpRoute
}
@Test
@Throws(Exception::class)
fun `Test SNMP endpoint`() {
mock.expectedBodiesReceived(SnmpConstants.SNMP_TRAP)
template.sendBody(SnmpConstants.MOCK_SNMP_ENDPOINT,
SnmpConstants.SNMP_TRAP)
mock.assertIsSatisfied()
verify(repository).save(PduEvent(1234, PDU.TRAP))
}
}
但是,当我运行此测试时,它会失败,因为repository
模拟从未有任何交互:
Wanted but not invoked:
repository.save(
PduEvent(requestId=1234, type=-89)
);
-> at org.meanwhile.in.hell.camel.snmp.route.SnmpRouteTest.Test SNMP endpoint(SnmpRouteTest.kt:61)
Actually, there were zero interactions with this mock.
有人可以帮我理解为什么这不能正确交互吗?手动运行时,这可以正常工作并按预期保存。
解决方案
现在我看到这里发生了什么!您RouteBuilder
的被测对象具有from("snmp")
. 如果您希望在那里传递模拟消息以进行测试,则需要在测试执行期间将组件与 a或组件之snmp:
类的东西交换。direct:
seda:
您当前的测试是将消息传递到 Mock 端点并验证它是否在那里收到。它不与真正的路由构建器交互。这就是为什么您的模拟端点断言确实通过但Mockito.verify()
失败了。
TL;博士
假设您使用的是 Apache Camel 3.x,这里是如何做到的。我不精通 Kotlin,所以我将展示如何在 Java 中做到这一点。
AdviceWithRouteBuilder.adviceWith(context, "route-id", routeBuilder -> {
routeBuilder.replaceFromWith("direct:snmp-from"); //Replaces the from part of the route `route-id` with a direct component
});
- 您需要修改您的路线构建器代码以将 ID 分配给路线(例如,
route-id
) - 将路由开头的 SNMP 组件替换为直接组件
- 将测试消息传送到
direct:
组件而不是 SNMP
TL;DR 结束。
下面是完整的示例代码。
PojoRepo.java
@Component
public class PojoRepo {
public void save(String body){
System.out.println(body);
}
}
SNMPDummyRoute.java
@Component
public class SNMPDummyRoute extends RouteBuilder {
PojoRepo pojoRepo;
public SNMPDummyRoute(PojoRepo pojoRepo) {
this.pojoRepo = pojoRepo;
}
@Override
public void configure() throws Exception {
from("snmp:0.0.0.0:1161?protocol=udp&type=TRAP")
.id("snmp-route")
.process(exchange -> {
exchange.getMessage().setBody(String.format("Saw message [%s]", exchange.getIn().getBody()));
})
.to("log:snmp-log")
.bean(pojoRepo, "save");
}
}
SNMPDummyRoteTest.java
注意:这个类使用CamelSpringBootRunner
而不是扩展CamelTestSupport
,但核心思想是相同的。
@RunWith(CamelSpringBootRunner.class)
@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
@DisableJmx(false)
@MockEndpoints("log:*")
public class SNMPDummyRouteTest {
@MockBean
PojoRepo repo;
@EndpointInject("mock:log:snmp-log")
MockEndpoint mockEndpoint;
@Produce
ProducerTemplate testTemplate;
@Autowired
CamelContext camelContext;
@Test
public void testRoute() throws Exception {
AdviceWithRouteBuilder.adviceWith(camelContext,"snmp-route",routeBuilder -> {
routeBuilder.replaceFromWith("direct:snmp-from");
});
testTemplate.sendBody("direct:snmp-from","One");
testTemplate.sendBody("direct:snmp-from","Two");
mockEndpoint.expectedMinimumMessageCount(2);
mockEndpoint.setAssertPeriod(2_000L);
mockEndpoint.assertIsSatisfied();
Mockito.verify(repo, Mockito.atLeast(2)).save(anyString());
}
}
下面是测试运行的日志。仔细查看 SNMP 端点与直接组件交换的 XML 部分。
2019-11-12 20:52:57.126 INFO 32560 --- [ main] o.a.c.component.snmp.SnmpTrapConsumer : Starting trap consumer on udp:0.0.0.0/1161
2019-11-12 20:52:58.363 INFO 32560 --- [ main] o.a.c.component.snmp.SnmpTrapConsumer : Started trap consumer on udp:0.0.0.0/1161 using udp protocol
2019-11-12 20:52:58.364 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route started and consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:58.368 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Total 1 routes, of which 1 are started
2019-11-12 20:52:58.370 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0-M4 (CamelContext: MyCamel) started in 2.645 seconds
2019-11-12 20:52:59.670 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Starting to graceful shutdown 1 routes (timeout 10 seconds)
2019-11-12 20:52:59.680 INFO 32560 --- [ - ShutdownTask] o.a.c.component.snmp.SnmpTrapConsumer : Stopped trap consumer on udp:0.0.0.0/1161
2019-11-12 20:52:59.683 INFO 32560 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy : Route: snmp-route shutdown complete, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.684 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Graceful shutdown of 1 routes completed in 0 seconds
2019-11-12 20:52:59.687 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route is stopped, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.689 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route is shutdown and removed, was consuming from: snmp://udp:0.0.0.0/1161
2019-11-12 20:52:59.691 INFO 32560 --- [ main] o.apache.camel.builder.AdviceWithTasks : AdviceWith replace input from [snmp:0.0.0.0:1161?protocol=udp&type=TRAP] --> [direct:snmp-from]
2019-11-12 20:52:59.692 INFO 32560 --- [ main] org.apache.camel.reifier.RouteReifier : AdviceWith route after: Route(snmp-route)[From[direct:snmp-from] -> [process[Processor@0x589dfa6f], To[log:snmp-log], Bean[org.foo.bar.POJORepo$MockitoMock$868728200]]]
2019-11-12 20:52:59.700 INFO 32560 --- [ main] org.apache.camel.reifier.RouteReifier : Adviced route before/after as XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="snmp-route">
<from uri="snmp:0.0.0.0:1161?protocol=udp&type=TRAP"/>
<process id="process1"/>
<to id="to1" uri="log:snmp-log"/>
<bean id="bean1" method="save"/>
</route>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<route xmlns="http://camel.apache.org/schema/spring" customId="true" id="snmp-route">
<from uri="direct:snmp-from"/>
<process id="process1"/>
<to id="to1" uri="log:snmp-log"/>
<bean id="bean1" method="save"/>
</route>
2019-11-12 20:52:59.734 INFO 32560 --- [ main] .i.e.InterceptSendToMockEndpointStrategy : Adviced endpoint [log://snmp-log] with mock endpoint [mock:log:snmp-log]
2019-11-12 20:52:59.755 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Route: snmp-route started and consuming from: direct://snmp-from
2019-11-12 20:52:59.834 INFO 32560 --- [ main] snmp-log : Exchange[ExchangePattern: InOnly, BodyType: String, Body: Saw message [One]]
2019-11-12 20:52:59.899 INFO 32560 --- [ main] snmp-log : Exchange[ExchangePattern: InOnly, BodyType: String, Body: Saw message [Two]]
2019-11-12 20:52:59.900 INFO 32560 --- [ main] o.a.camel.component.mock.MockEndpoint : Asserting: mock://log:snmp-log is satisfied
2019-11-12 20:53:01.903 INFO 32560 --- [ main] o.a.camel.component.mock.MockEndpoint : Re-asserting: mock://log:snmp-log is satisfied after 2000 millis
2019-11-12 20:53:01.992 INFO 32560 --- [ main] o.a.c.s.boot.SpringBootCamelContext : Apache Camel 3.0.0-M4 (CamelContext: MyCamel) is shutting down
2019-11-12 20:53:01.993 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Starting to graceful shutdown 1 routes (timeout 10 seconds)
2019-11-12 20:53:01.996 INFO 32560 --- [ - ShutdownTask] o.a.c.i.engine.DefaultShutdownStrategy : Route: snmp-route shutdown complete, was consuming from: direct://snmp-from
2019-11-12 20:53:01.996 INFO 32560 --- [ main] o.a.c.i.engine.DefaultShutdownStrategy : Graceful shutdown of 1 routes completed in 0 seconds
推荐阅读
- low-latency - 用于估计启用 NUMA 的改进的 Memory Profiler 工具
- java - 如何使用 Selenium 框架从受用户名和密码保护的站点下载文件?
- hbase - MultiRowRangeFilter:如何限制每个 RowRange 的结果?
- c# - 如何使用 Display() 方法或任何替代方法?
- python - MacOS 上的 PyCharm 无法处理文件
- rest - 将活动目录中的员工数据放入共享点中的 html 页面
- c# - 制作静态类当前状态的快照对象
- android - 如何在 Android N 中获得 30 分钟的时间选择器间隔
- mysql - 在 MYSQL 中的 JSON 字段中搜索值
- node.js - 从特定目录内的所有目录加载文件