蒼時弦也
蒼時弦也
資深軟體工程師
發表於

驗收測試 - 重新思考 Rails 架構

這篇文章是 重新思考 Rails 架構 系列的一部分。

在實際開始實作之前,透過測試確認行為以及開發過程中進行驗證都會是個不錯的方式。我們會透過 Cucumber 的文件測試法中的方式,來描述一個運送狀態更新的設計。

描述功能

假設我們要實現一個跨時區的物流運送流程,那麼可以撰寫如下的測試。

 1Feature: Shipment
 2  Scenario: I can update the simpment route
 3    Given there have some shipment
 4      | id |  state    |
 5      | 1  |  shipping |
 6    And there have some shipment route
 7      | route_id | shipment_id | delivered_at         |
 8      | OKA      | 1           | 2024-03-19T00:00:00Z |
 9    When I make a POST request to "/shipments/1/routes"
10      """
11      {
12        "route_id": "TPE",
13        "date": "0319",
14        "time": "1000"
15      }
16      """
17    Then the response should be
18      """
19      {
20        "id": 1,
21        "state": "shipping",
22        "routes": [
23          {
24            "route_id": "OKA",
25            "shipment_id": 1,
26            "delivered_at": "2024-03-19T00:00:00.000Z"
27          },
28          {
29            "route_id": "TPE",
30            "shipment_id": 1,
31            "delivered_at": "2024-03-19T02:00:00.000Z"
32          }
33        ]
34      }
35      """

上面的例子我們透過描述 API 的行為,來呈現一些資訊。

  • 運送狀態是對應某筆訂單
  • 運送過程會有多個路徑(Route)
  • 填入時間時,會以 datetime 的格式搭配,並且以當地時間為基準

除此之外,在系統的設計中我們預期 Order 和 Shipment 是一對一的關係,因此在這裡不會有 order_id 的欄位,從實體(Entity)的角度來看辨識 Order 和辨識 Shipment 是相同的。

然而,在 Route 的觀點來看,一個實體會是 Shipment ID + Route ID 的組合,上述的情境中我們仍可以使用資料庫的 Auto Increment 機制來管理,然而在實際使用時不一定會直接參考資料庫的流水號。

針對回傳內容的檢查,因為完整比對 JSON 有時候不一定是個好的處理方式,可以考慮利用 JMESPath 這類套件,使用 routes[0].route_id 來找到 OKA 進行比對,會相對有彈性

步驟定義

有了測試的步驟後,就可以來撰寫步驟定義。

 1Given('there have some shipment') do |table|
 2  table.hashes.each do |row|
 3    Shipment.create!(**row)
 4  end
 5end
 6
 7Given('there have some shipment route') do |table|
 8  table.hashes.each do |row|
 9    ShipmentRoute.create!(**row)
10  end
11end
12
13When('I make a POST request to {string}') do |path, raw_body|
14  body = JSON.parse(raw_body)
15  @response = post path, body
16end
17
18Then('the response should be') do |raw_body|
19  expected = JSON.parse(raw_body)
20  actual = JSON.parse(@response.body)
21
22  expect(actual).to eq(expected)
23end

我們直接透過 ActiveRecord 定義的 Model 來生成測試用的資料,並且直接對回傳的內容轉換成 JSON 進行比對,這樣就可以有初步的雛形。

實際開發時會盡可能推遲 Model 層級的實作,因為這系列的重點不在測試的實作,因此會跳過比較多細節。

接下來就可以切入到 Controller 的部分來進行實作,至於測試案例可以根據情況在這個階段多描述並以 @wip 標記註記會在之後處理,或者在後續開發時有發現新的案例時補上。