如何使用 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 上找到。