Apache Camel的單元測試
幾乎大多數軟體開發人員都知道Apache Camel是一個事件驅動的框架,具有基於規則的路由和調解引擎,它由Java驅動,許多人都受益於它在與Spring整合方面提供的巨大支援。
但是如何進行單元測試呢?
我們對Camel的測試就像我們對任何java方法進行單元測試一樣,透過傳遞輸入和斷言響應來進行。Camel測試是一個強大的模組,由其開發者提供,使單元測試的工作與它的使用無縫銜接。
在這篇文章中,我將帶你瞭解3個常見的場景,這些場景幾乎在每個使用Camel的應用程式中都是最廣泛使用的,並使用Camel的測試模組對它們進行單元測試。完整的程式碼庫可以在這裡找到。
在此之前,請確保你的專案包含了正確的Camel測試模組依賴,如這裡所見。在這篇文章中,我將使用Camel與Junit5和執行在Maven之上的Spring相結合。因此,我的選擇是下面這個。
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-test-spring-junit5</artifactId> <version>${camel.version}</version> <scope>test</scope> </dependency> |
場景-1
我猜這將是Camel被廣泛利用的最流行的用例。使用Camel的檔案元件從輸入路徑中挑選一個檔案並進一步處理。在這個特定的案例中,我們從輸入路徑中挑選一個檔案,並觀察其格式,如果是CSV,就輸出Success,如果是XLSX,就輸出Fail,如果是其他檔案,就不處理。同樣的程式碼如下。
String fileInbound = UriComponentsBuilder.newInstance().scheme("file") .path(Paths.get(inboundPath).toAbsolutePath().toString()) .queryParam("include", "^.*\\.(xlsx|csv)$") .build().toString(); from(fileInbound).id("SAMPLE_FILE_ROUTE") .log("Received file: ${header.CamelFileName}") .choice() .when((exchange) -> exchange.getIn().getHeader("CamelFileName").toString() .endsWith("csv")).setHeader("status", simple("SUCCESS")).log("Hurray !! Its a CSV !!") .otherwise().setHeader("status", simple("FAILURE")).log("Oh No!! Its an XLSX..") .end(); |
單元測試將是相當直接的。模仿上述路由在測試中執行時的行為方式,然後丟擲一個檔案,觀察上述所有場景的正確行為,即CSV的成功,XLSX的失敗和txt檔案的不拾取。這可能看起來很可怕,但是Camel測試模組提供了一個令人興奮的方法,在測試時像正常執行一樣啟動路由,並允許在檔案拾取時驗證正/負場景,允許將最終響應路由到一個模擬端點。
實現如下:
//Extend the class with CamelTestSupport which Camel Test provides class SampleFileRouteTest extends CamelTestSupport { //Override the createRouteBuilder() and return an object of the actual route class under test @Override protected RoutesBuilder createRouteBuilder() throws Exception { return new SampleFileRoute("src/test/resources"); } //Set-Up the mock route to listenForCompletion response once the route is triggered @Override protected void doPostSetup() throws Exception { AdviceWith.adviceWith(context, "SAMPLE_FILE_ROUTE", a -> a.onCompletion().onCompleteOnly().to("mock:listenForComplete")); } //And Finally the test cases for all 3 scenarios, namely Success/Fail and Invalid File @Test void testForSuccessValidFile() throws IOException, InterruptedException { inboundFile = new File(inboundPath.toString(), "test.csv"); inboundFile.createNewFile(); listenForComplete.expectedHeaderReceived("status", "SUCCESS"); listenForComplete.assertIsSatisfied(); } @Test void testForFailValidFile() throws IOException, InterruptedException { inboundFile = new File(inboundPath.toString(), "test.xlsx"); inboundFile.createNewFile(); listenForComplete.expectedHeaderReceived("status", "FAILURE"); listenForComplete.assertIsSatisfied(); } @Test void testForInvalidFile() throws IOException, InterruptedException { inboundFile = new File(inboundPath.toString(), "test.txt"); inboundFile.createNewFile(); listenForComplete.expectedHeaderReceived("status","SUCCESS"); listenForComplete.assertIsNotSatisfied(); } } |
場景-2
另一個有趣的用例是FTP,我廣泛地看到人們在使用Camel。Camel提供了一個叫FTP的元件,它以簡單和非常乾淨的方式為所有的FTP傳輸提供了開箱即用的支援。我們在這方面的用例將是相當直接的。從入站路徑中選取一個檔案並將其SFTP到遠端伺服器的一個路徑。成功時列印出一個日誌,表明傳輸是透過的,失敗時列印同樣的日誌。下面是這個邏輯的程式碼。
//Prepare the respective inbound and outbound URLS String fileInbound = UriComponentsBuilder.newInstance().scheme("file") .path(inboundPath) .build().toString(); String outboundLocation = UriComponentsBuilder.newInstance() .scheme("sftp").host(sftpInfo.getHost()).port(sftpInfo.getPort()).userInfo(sftpInfo.getUser()) .path(sftpInfo.getPath()).queryParam("preferredAuthentications", "password") .queryParam("password", sftpInfo.getPassword()) .queryParam("jschLoggingLevel", "WARN") .build().toString(); //Use Camel-FTP Support and effect the transfer.. namely pick from the inbound and send to output //and observe success/failures from(fileInbound).id("SAMPLE_SFTP_ROUTE") .log("Starting SFTP Transfer for File: ${header.CamelFileName}") .to(outboundLocation) .log("SFTP Transfer Success for File: ${header.CamelFileName}"); onException(Exception.class) .maximumRedeliveries(2) .redeliveryDelay(20) .log("SFTP Transfer Failure for File: ${header.CamelFileName}") .handled(true); |
測試案例也很簡單,就是觀察連線到SFTP伺服器的正常啟動路線,以及正向流量的正常傳輸,而對於負向流量,路線無法啟動,網路斷開,意味著傳輸沒有發生。此刻你可能會想到我們將在這裡使用的SFTP伺服器的下落。對於那些聽說過fake-sftp-server-lambda這個記憶體SFTP解決方案的人來說,這並不奇怪。對於那些第一次聽說的人來說,fake-sftp-server是一個MIT許可的專案,它寫在Apache SSHD專案的SFTP伺服器之上,以方便在測試執行中使用記憶體SFTP伺服器。利用Camel和Fake-Sftp-Server的力量,對同樣的測試進行了如下操作。
//Extend the test class with Test Support Camel provides class SampleSftpRouteTest extends CamelTestSupport { //Start the in-memory SFTP Server using Fake-Sftp-Server-Lambda and make it listen @Override @BeforeEach public void setUp() throws Exception { withSftpServer(server -> { server.addUser("tst", "tst"); sftpInfo = SftpInfo.builder().host("0.0.0.0").port(server.getPort()).path("/").build(); }); super.setUp(); } //Test Both the Success and Fail Scenarios. //For Success - Test if the file is transfered successful upon created in I/P Path //For Failure - Test if the route throws exception upon start and the file transfer does'nt happen @Test void testsftpSuccess() throws Exception { inboundFile = new File(inboundPath.toString(), "test.txt"); inboundFile.createNewFile(); await().atMost(10, TimeUnit.SECONDS); withSftpServer(server -> assertTrue(server.existsFile("/test.txt"))); assertFalse(inboundFile.exists()); } @Test void testsftpFailure() throws Exception { sftpInfo.setUser("user"); sftpInfo.setPassword("wrong"); inboundFile = new File(inboundPath.toString(), "test.txt"); inboundFile.createNewFile(); await().atMost(10, TimeUnit.SECONDS); withSftpServer(server -> assertFalse(server.existsFile("/test.txt"))); assertTrue(inboundFile.exists()); } } |
場景-3
這無疑是最有趣的方面,對於大多數開發者來說,使用Camel是為了它提供的多跳支援。當生產者傳送訊息交換時,Camel Direct提供了對任何消費者的直接、同步的呼叫。這個端點可用於連線同一Camel上下文中的現有路由。在我們的用例中,我們實現了所提供的設施,並以同步的方式在各個邏輯塊中進行路由。這些程式碼或多或少都是不言自明的。不過,如果有不清楚的地方,請隨時檢視評論。
//Build the route as below. It all starts from India and proceeds to Delhi, Mumbai, Kolkata and Chennai //In each and every stop, it goes into individual logics and has it executed. //The block of individual logic for Delhi is also given below. from("direct:India").id("SAMPLE_MULTIHOP_ROUTE") .log("Welcome to India !!") .to("direct:Delhi") .to("direct:Mumbai") .to("direct:Kolkata") .to("direct:Chennai"); from("direct:Delhi") .bean(Delhi.class); from("direct:Mumbai") .bean(Mumbai.class); from("direct:Kolkata") .bean(Kolkata.class); from("direct:Chennai") .bean(Chennai.class); //Execution Logic inside Delhi.class @Component public class Delhi { @Handler public void handler(@RequestBody String name) { System.out.println("Hello Mr " + name + "!!! Welcome to Delhi !!"); } } |
測試這一點非常簡單,使用Camel的Producer-Template明確地觸發起始點,然後模擬所有其他停止點的豆子,並驗證與模擬物件的互動。注意,重要的是在Camel測試上下文中注入被模擬的物件,以觀察和斷言其行為。同樣的測試將如下所示。
@ExtendWith(MockitoExtension.class) class SampleMultiHopRouteTest extends CamelTestSupport { @InjectMocks private SampleMultiHopRoute sampleMultiHopRoute; @Mock private Delhi delhi; @Mock private Mumbai mumbai; @Mock private Kolkata kolkata; @Mock private Chennai chennai; @Override protected RoutesBuilder createRouteBuilder() throws Exception { Registry registry = context.getRegistry(); registry.bind("delhi", Delhi.class, delhi); registry.bind("mumbai", Mumbai.class, mumbai); registry.bind("kolkata", Kolkata.class, kolkata); registry.bind("chennai", Chennai.class, chennai); return sampleMultiHopRoute; } @Test void testMultiHopRoute() { doNothing().when(delhi).handler("Junit"); doNothing().when(mumbai).handler("Junit"); doNothing().when(kolkata).handler("Junit"); doNothing().when(chennai).handler("Junit"); template.sendBody("direct:India", "Junit"); verify(delhi).handler("Junit"); verify(mumbai).handler("Junit"); verify(kolkata).handler("Junit"); verify(chennai).handler("Junit"); } } |
還有許多其他驚人的用例,Camel實際上被用於這些用例。當然,所有這些都有非常令人興奮的方法來進行單元測試,Camel-Test在每一個單元測試中都發揮了巨大的作用,從提供一個正確的環境來實時進行單元測試到斷言這些測試。是的!有一些調整是必須做的,以模擬用例的實際工作方式,使其得到全面的測試。但無論如何,Camel-測試支援是其開發團隊提供的一個奇妙的功能,使其測試強大而簡單。
相關文章
- apache camelApache
- 單元測試:單元測試中的mockMock
- 測試 之Java單元測試、Android單元測試JavaAndroid
- 單元測試-【轉】論單元測試的重要性
- 單元測試,只是測試嗎?
- 基於Spring Integration和Apache Camel的SEDASpringApache
- Apache Camel日誌四種方法Apache
- 單元測試-一份如何寫好單元測試的參考
- 單元測試的規範
- java中的單元測試Java
- golang單元測試Golang
- 單元測試真
- iOS 單元測試iOS
- python 單元測試Python
- 前端單元測試前端
- Flutter 單元測試Flutter
- 單元測試 Convey
- 單元測試工具
- 聊聊單元測試
- 十五、單元測試
- Go單元測試Go
- SpringBoot單元測試Spring Boot
- 程式碼重構與單元測試——重構1的單元測試(四)
- Vue 應用單元測試的策略與實踐 04 - Vuex 單元測試Vue
- 前端測試:Part II (單元測試)前端
- Vue 應用單元測試的策略與實踐 02 - 單元測試基礎Vue
- Vue 應用單元測試的策略與實踐 03 - Vue 元件單元測試Vue元件
- 如何寫出好的單元測試
- Go語言中的單元測試Go
- Junit單元測試—MavenMaven
- 單元測試框架 mockito框架Mockito
- 單元測試與MockitoMockito
- Source Generator 單元測試
- 單元測試?即刻搞定!
- 聊聊前端單元測試前端
- 單元測試基礎
- JavaScript單元測試框架JavaScript框架
- 單元測試 -- mocha + chaiAI