如何使用 Moco 輕鬆設定存根伺服器
一、簡介
在軟體開發過程中,幾乎不可能低估測試的重要性。如果我們談論測試,就很難忽視模擬工具,它們通常在確保強大的覆蓋範圍方面發揮重要作用。
本文探討了Moco ,這是一種多功能且輕量級的模擬工具,可簡化各種協定的模擬伺服器。
我們將探討 Moco 的功能以及如何開始使用,並透過實際範例來說明其功能。
2. 莫科是什麼?
Moco 是一個開源模擬庫,允許我們存根和模擬服務。它重量輕,使用和配置極為簡單。
Moco 支援多種協議,例如 HTTP、HTTPS 和套接字,使其成為測試從 Web 服務到即時通訊應用程式等不同類型應用程式的絕佳選擇。
3. Moco 入門
Moco 的主要優點之一是易於設定和使用。將其整合到新項目或現有項目中非常簡單。
3.1. JSON 設定和獨立伺服器
在深入研究程式碼之前,值得注意的是Moco 允許我們使用 JSON 編寫配置並獨立運行它們。這有利於快速設置,無需任何編碼。
以下是我們可以在config.json
檔案中編寫的 JSON 配置的簡單範例:
[
{
"request": {
"uri": "/hello"
},
"response": {
"text": "Hello, Moco!"
}
}
]
要使用此配置啟動伺服器,我們運行moco-runner
:
java -jar moco-runner-1.5.0-standalone.jar http -p 12345 -c config.json
此命令使用提供的config.json
在連接埠12345
上執行 HTTP 伺服器。模擬將在http://localhost:12345/hello
上提供。
這些獨立設定提供了極大的靈活性,並獲得與程式碼配置相同級別的開發人員支援。
3.2.設定
要開始將 Moco 與 Maven 一起使用,我們將包含此依賴項:
<dependency>
<groupId>com.github.dreamhead</groupId>
<artifactId>moco-runner</artifactId>
<version>1.5.0</version>
<scope>test</scope>
</dependency>
對於 Gradle 項目,我們需要使用這個:
testImplementation 'com.github.dreamhead:moco-runner:1.5.0'
4. Java 的實際例子
Moco 擁有豐富的 Java API,這為我們定義模擬資源提供了巨大的自由度。但現在,讓我們透過一些簡單的範例來檢查伺服器初始化是如何運作的。
4.1.伺服器初始化
我們必須了解如何根據協定設定伺服器以使用Java初始化Moco伺服器。這是一個快速概述:
HttpServer server = Moco.httpServer(12345); // Initialize server on port 12345
server.request(Moco.by(Moco.uri("/hello")))
.response(Moco.text("Hello, Moco!")); // Set up a basic response
Runner runner = Runner.runner(server);
runner.start(); // Start the server
在此範例中,我們首先在連接埠12345
上初始化 HTTP 伺服器。然後,我們將伺服器配置為回應“ Hello, Moco!
” 每當它請求/hello
端點時。最後,我們使用 Runner 的start()
方法啟動伺服器.
另外,我們不應該忘記在完成後停止伺服器:
runner.stop();
例如,在測試中,我們可以將其放入帶有@AfterEach
註解的方法中。對於HTTPS,我們需要先建立一個證書,用HttpsCertificate
物件表示,然後再使用httpsServer()
方法:
HttpsCertificate certificate = certificate(pathResource("certificate.jks"),
"keystorePassword", "certPassword");
HttpsServer server = httpsServer(12346, certificate);
如果我們想使用socket連接,Moco提供了socketServer()
:
final SocketServer server = socketServer(12347);
我們也可以在伺服器建立過程中使用先前提到的 JSON 設定:
final HttpServer server = jsonHttpServer(12345, file("config.json"));
4.2. HTTP 程式碼和回應
現在我們已經有了基本設置,讓我們探索更複雜的範例。讓我們回傳一個 JSON 回應:
server.request(by(uri("/api/user")))
.response(header("Content-Type", "application/json"))
.response(json("{\"id\": 1, \"name\": \"Ryland Grace\", \"email\": \"[email protected]\"}"));
如果我們將 JSON 儲存在檔案中,我們可以提供它的路徑:
server.request(by(uri("/api/user")))
.response(header("Content-Type", "application/json"))
.response(Moco.file("src/test/resources/user.json"));
Moco 回應的預設 HTTP 代碼是200
。但它允許我們更改它,例如重現錯誤回應:
server.request(Moco.match(Moco.uri("/unknown"))).response(Moco.status(404), Moco.text("Not Found"));
另外,在上面的範例中,我們沒有指定 HTTP 方法,這意味著使用了 GET。要模擬 POST 請求,我們可以**使用post()
而不是request()
** 。事實上,在前面的範例中,我們可以明確地使用get()
。
讓我們來看一個 POST 模擬範例:
server.post(by(uri("/resource"))).response("resource updated");
Moco 對於 GET、POST、PUT 和 DELETE 有單獨的get(), post(), put()
和delete()
方法。
此外,我們可以指定 Moco 進行檢查的內容:
server.request(json(file("user.json"))).response("resource updated");
4.3.更多微調
上面的例子只是觸及了 Moco 潛力的冰山一角。事實上, Moco 允許我們以多種方式微調我們的伺服器。例如,我們可以為預期請求配置查詢參數、cookie、各種媒體類型、自訂條件等。
在此範例中,我們使用JSONPath**匹配請求**:
server.request(eq(jsonPath("$.item[*].price"), "0")).response("we have free item");
在配置回應時,我們可以模擬代理、重定向、檔案附件、更改回應延遲、循環返回等。 :
// First request will get "Alice", second and third will get "Bob" and "Clyde". Forth request will return "Alice" again.
server.request(by(uri("/user"))).response(seq("Alice", "Bob", "Clyde"));
5. 在單元測試中使用 Moco
Moco 與 JUnit 無縫整合。我們可以透過將 Moco 伺服器嵌入到測試生命週期中來有效地模擬外部服務。這是我們如何做到這一點的一個簡單範例:
public class MocoUnitTest {
private Runner runner;
@BeforeEach
public void setup() {
HttpServer server = Moco.httpServer(12345);
server.request(Moco.by(Moco.uri("/test"))).response("Test response");
runner = Runner.runner(server);
runner.start();
}
@AfterEach
public void tearDown() {
runner.stop();
}
// Our tests
}
在設定中,我們建立一個 HTTP 伺服器,它使用「 Test response
」來回應/test
處的請求。我們在每次測試之前啟動該伺服器並在之後停止它。
現在,讓我們嘗試測試我們的伺服器:
@Test
void givenMocoHttpServer_whenClientSendsRequest_thenShouldReturnExpectedResponse() throws Exception {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:12345/test"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals("Test response", response.body());
}
在此範例中,我們使用 Java 的內建HttpClient
向 Moco 伺服器傳送 HTTP 請求。這種方法避免了額外的依賴關係,使其成為在單元測試中測試 HTTP 互動的絕佳選擇。
5.1.與@Rule
一起使用
Moco 在 JUnit 4 中使用TestRule
來簡化整合。 MocoJunitRunner
提供了多種方法來運行 Moco 伺服器作為TestRule
,它在我們的測試之前啟動伺服器並在測試之後停止它:
public class MocoJunitHttpUnitTest {
private static final HttpServer server = httpServer(12306);
static {
server.response(text("JUnit 4 Response"));
}
@Rule
public MocoJunitRunner runner = MocoJunitRunner.httpRunner(server);
@Test
public void givenMocoServer_whenClientSendsRequest_thenShouldReturnExpectedResponse() throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:12306"))
.build();
HttpResponse<String> response = client
.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals(response.body(), "JUnit 4 Response");
}
}
5.2. JUnit 5 集成
對於 JUnit 5,Moco 使用@ExtendWith
註解來整合其伺服器控制項:
@ExtendWith(MocoExtension.class)
@MocoHttpServer(filepath = "src/test/resources/foo.json", port = 12345) // Load JSON config from file system
public class MocoHttpTest {
@Test
public void givenMocoServer_whenClientSendsRequest_thenShouldReturnExpectedResponse() throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:12345/hello"))
.build();
HttpResponse<String> response = client
.send(request, HttpResponse.BodyHandlers.ofString());
assertEquals("Hello, Moco!", response.body());
}
}
@MocoHttpServer
註解指定伺服器配置,確保為每個測試正確設定和拆除它。
6. 使用 Moco 時的良好實踐
雖然 Moco 提供了強大的配置,但我們的目標應該是保持模擬設定簡單且易於管理。除非必要,否則我們應該避免過於複雜的配置,因為它們可能會變得難以維護和理解。此外,我們應該考慮將 JSON 檔案用於簡單場景,並使用基於 Java 的配置來滿足更動態或複雜的需求。 JSON 檔案可以整齊地儲存在具有簡潔名稱的單獨目錄中。
在並發運行**時使用唯一連接埠隔離測試**是一個很好的做法。在這種情況下,我們應該確保每個 Moco 伺服器實例使用唯一的連接埠。這可以防止連接埠衝突並確保測試不會相互幹擾。我們可以使用組態管理庫來動態處理連接埠分配。
我們應該使用 HTTPS 來進行安全通訊。如果我們的應用程式透過 HTTPS 進行通信,我們也應該將 Moco 伺服器設定為使用 HTTPS。這確保我們的測試準確反映生產環境。
值得考慮啟用日誌記錄來追蹤傳入的請求和回應;它可能有助於快速診斷問題:
HttpServer server = httpServer(12345, log()); // we can also specify file for log output as a String parameter in log()
七、結論
在本教程中,我們探索了 Moco,這是一個強大而靈活的框架,可以簡化 Java 應用程式中的模擬。無論我們需要模擬 HTTP、HTTPS 還是套接字伺服器,Moco 都提供簡單的 API 並與 JUnit 無縫整合。我們也一睹了 Moco 為我們提供的強大工具箱;它允許我們針對許多不同的現實場景微調我們的模擬伺服器。
透過將 Moco 納入我們的測試策略中,我們可以增強測試的可靠性和可維護性,使開發更加高效和穩健。
與往常一樣,這些範例可以在 GitHub 上找到。